1

If I am ever working with rxjs, I normally, in my component file, keep track of all of my subscriptions by pushing said subscriptions into a Subscription array:

private subcriptions: Subscription[];

this.subscriptions.push(this.myObservableOne.subscribe(...));
this.subscriptions.push(this.myObservableTwo.subscribe(...));

public ngOnDestroy(): void {
    this.subscriptions.forEach(s => s.unsubscribe());
}

Now, I am running into something that I thought was interesting. If I instead need to subscribe to an Observable from a service:

this.myService.myObservable.subscribe(...)

Who owns the subscription? Will my component own it, or will the service own it? In other words, will my service need to implement ngOnDestroy to unsubscribe from that Observable?

I feel like doing: this.subscriptions.push(this.myService.myObservable.subscribe(...)); might work but I am not entirely sure.

User 5842
  • 2,849
  • 7
  • 33
  • 51

2 Answers2

2

Calling .subscribe(...) on an Observable returns a Subscription.

If you don't assign this subscription to a variable (or in your case push it to an array of subscriptions), you will lose reference to this subscription, hence you can't unsubscribe from it, which can cause memory leaks.

So your assumption of pushing it into array of subscriptions:

this.subscriptions.push(this.myService.myObservable.subscribe(...));

and unsubscribing in ngOnDestroy of your component is correct.

belicam
  • 732
  • 6
  • 9
  • 1
    FWIW instead of maintianing your own subscriptions array and looping over it, just keep a "subscription" and add these subscriptions to it. Less work for you: `this.subscriptions = new Subscription(); this.subscriptions.add(observable.subscribe(...));` and later `this.subscriptions.unsubscribe(); // unsubscribes from all of them. This pattern also can handle some subtle use cases where you might add your subscription *after* the master subscription has been unsubscribed. – Brandon Jul 24 '18 at 18:30
1

Another option is to use takeWhile or something similar with a flag such as the componentActive flag below. Something like the code shown below.

This completes the subscription so you don't need to unsubscribe.

import { Component, OnInit, OnDestroy } from '@angular/core';

import { Product } from '../product';

import { Observable } from 'rxjs';
import { takeWhile } from 'rxjs/operators';

/* NgRx */
import { Store, select } from '@ngrx/store';

@Component({
  selector: 'pm-product-list',
  templateUrl: './product-list.component.html',
  styleUrls: ['./product-list.component.css']
})
export class ProductListComponent implements OnInit, OnDestroy {
  componentActive = true;

  // Used to highlight the selected product in the list
  selectedProduct: Product | null;

  constructor(private store: Store<fromProduct.State>) { }

  ngOnInit(): void {

    // Subscribe here because it does not use an async pipe
    this.store.pipe(
      select(fromProduct.getCurrentProduct),
      takeWhile(() => this.componentActive)
    ).subscribe(
      currentProduct => this.selectedProduct = currentProduct
    );

  }

  ngOnDestroy(): void {
    this.componentActive = false;
  }

}
DeborahK
  • 57,520
  • 12
  • 104
  • 129
  • 1
    This should be the way to do it. This article by Ben Lesh does a good explaining of this: https://medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87 – DavidZ Jul 24 '18 at 19:49
  • 1
    We use this approach throughout our application - it works perfectly, nice and clean. The only time we store subscriptions now is if we manually need to cancel a subscription for functional reasons within a component's lifetime. – Mark Hughes Jul 25 '18 at 09:44