Auto Unsubscribing From Observables On NgDestroy

A common pattern when working with observables/subjects inside an Angular Component, is to store the subscriptions in a list, and then unsubscribe when the component is destroyed.

Now I’ve seen many developers not bother unsubscribing at all when it comes to HttpClient requests, but it’s a good habit to get into as more often than not, you will get to a point where you are using Observables/Subjects for more than just simple API calls, and then you will really need to unsubscribe!

A common example of how many developers implement this is like so :

export class AppComponent implements OnInit, OnDestroy {

  subscriptions : Subscription[];

  constructor(private myService : MyService)
  {
  }

  ngOnInit() {
    let subscription = this.myService.getData().subscribe(x => {
      //Do something with the data. 
    });
    this.subscriptions.push(subscription);
  }

  ngOnDestroy() {
    this.subscriptions.forEach(x => {
      if(!x.closed) {
        x.unsubscribe();
      }
    });
  }
}

A better (just in my opinion) option is typically to use a simple boolean variable to close off all subscriptions when the component is destroyed. This is used in conjunction with the takeWhile operator of RxJS like so :

export class AppComponent implements OnInit, OnDestroy {
  isAlive : boolean = true;

  constructor(private myService : MyService)
  {
  }

  ngOnInit() {
    this.myService.getData().pipe(takeWhile(() => this.isAlive)).subscribe(x => {
      //Do something with the data. 
    });
  }

  ngOnDestroy() {
    this.isAlive = false;
  }
}

This is a better option in my opinion because you don’t need to keep a list in memory of subscriptions and there is slightly less boilerplate code going on. But still, we have to litter our components with this “isAlive” flag everywhere, and remember to add an ngOnDestroy that sets it to false. You could create base components that exposes this flag, but it’s still a bit of boilerplate that you could do without.

What we really are looking for is a nice library to do all of this for us. As it so happens, I found one called “TakeWhileAlive”. It’s NPM page is here : https://www.npmjs.com/package/take-while-alive. At first this worked great! That is, until I deployed it into production and started seeing weird happenings. As it turns out, it doesn’t work once you deploy the project in production mode : https://github.com/joostme/takeWhileAlive/issues/2 doh!

Luckily, that Github issue pointed me to another great library. “TakeUntilDestroy“. It works like this.

If Angular 8 or below :

npm install ngx-take-until-destroy --save

If Angular 9 or above then :

npm install @ngneat/until-destroy

Then inside your component, if you are using Angular 8, you only need to do the following :

import { untilDestroyed } from 'ngx-take-until-destroy';

this.myService.getData().pipe(untilDestroyed(this)).subscribe(x => {
  //Do something with the data. 
});

And that’s it. The one caveat is that you *must* have an empty ngOnDestroy method, even if it’s not being used. The library will throw an error if this is not the case. But otherwise, you will now auto unsubscribe without any additional boilerplate just by adding untilDestroyed onto all of your subscriptions.

Now for Angular 9+, things are a little more complicated, but still rather easy. The only additional logic you need is to add a single attribute onto your component like so :

@Component(...)
@UntilDestroy()
export class AppComponent implements OnDestroy {
 //...
}

Notice how we added UntilDestroy() onto our component. Then we still add untilDestroyed(this) onto our subscriptions similar to Angular 8, and we will auto unsubscribe when our component is destroyed.

Leave a Reply

Your email address will not be published. Required fields are marked *