How DestroyRef Made My Life Easier with Angular 16

No need to use ngOnDestroy lifecycle-hook for unsubscribing anymore

Kamil Konopka
Bits and Pieces

--

Photo by Kevin Ku on Unsplash

We all know the story behind the necessity of completing subscriptions when the component is being destroyed, otherwise, we will introduce memory leaks and our application/browser/client’s machine will become slower and slower, due to loads of garbage within the memory.

There’ve been plenty of techniques since Observable Pattern became popular enough to be shipped with the most recent version of Angular at a time (game-changing version 2, now it’s already 16! Can you believe it?):

  • Using Subscription instance — we had to declare a new Subscription instance, assign the actual one and call its unsubscribe method when the component had been destroyed. This approach had its own limitations as you would have to declare separate subscription attributes per assignment as presented in the example below:
Single subscription handled with Subscription class instance example
  • Using subscriptions array — in this scenario, we are able to leverage as many subscriptions as we want with single subscriptions attribute. (Not bad right?) But if you think of clean code and readability, this doesn’t seem to be an ideal option either, as our code is being already wrapped with the push array method, yet an indentation has to be made. With a simple example as below it doesn’t seem to be an issue, but just imagine some more complex operations on multiple streams. Not to forget, we have to unsubscribe from each (sub) when the component is being destroyed like so:
Multiple subscription handled with subscriptions array
  • Using takeUntil rxjs operator combined with Subject — instead of subscription instance. We now use a new subject instance. I’ve seen multiple different flavors of type declaration used within. I think the most accurate is void as we do not really need an actual value, just an emission itself. Next, we need to remember to hook up takeUntil operator into the pipe. And make sure it will be the last one, cause if you’re using another operator returning higher-order observable like switchMap, it might not complete your stream, switchMap will be still operating. With this technique, we still keep the ngOnDestroy lifecycle-hook, but this time, calling the next method on our subject.

There is no need to complete subject stream, as there is no actual subscription to it being made. Therefore, no need to call: this.destroy.complete() at all.

P.S. If you’re not sure if your subscription is completed, you can always use finalize rxjs operator, which will gets triggered when your subscription completes. Voila! We’re safe :-)

Subject based subscription handling example

I don’t have to remind, that those activities had to be replicated within every component, where subscriptions are being used! Seems like additional activity to perform and extra code to add and remember about.

As developers, we want to constantly improve and make our life as easy as possible. I’ve already seen implementations where base component is being introduced just to keep the subscription implementation within one place only. To be honest, personally I’m not a huge fun of mentioned approach as we are already introducing additional layer of abstraction and inheritance which will be shipped to every component, complicating unit testing and bringing additional bits for super constructor calls.

This also opens pandora box, for less experienced developers to add some more to this base component (There is always something to add there!).

I’ve also seen some implementations using @Decorators and custom rxjs operators, which is a definitely leaner approach, but then you were forced to maintain it on your own and figure out the approach to reuse it within multiple projects (there are couple of techniques to approach it as well, but it’s a story for another article).

There’s already library in place from Netanel Basal so called @ngneat/until-destroy which brought a nice experience to our code base. I’ve already used it in couple of projects as well. One remark on my end, you really need to understand how it works to benefit from it, otherwise memory leaks, here we go!

Finally, Angular 16 was released with an amazing DestroyRef provider! Which can be easily used, we do not need much, just an injection token with DestroyRef and takeUntilDestroyed operator straight from @angular/core/rxjs-interop package. You have to see it in action! No need to use ngOnDestroy lifecycle-hook for unsubscribing anymore.

DestroyRef based subscription handling available since Angular 16 release example

💡 You can even go further and create your own Rxjs operator, which will be super simple and easy to maintain! Use DestroyRef to create an operator called untilDestroyed, for example. You can then use an open-source toolchain like Bit to generate its documentation automatically, then publish, version, and reuse it across all of your projects with a simple bit import your.username/untilDestroyed command.

Find out more here:

Thank you Angular team! This will significantly reduce my code base!

Read more of my articles here!:

Build Angular Apps with reusable components, just like Lego

Bit’s open-source tool help 250,000+ devs to build apps with components.

Turn any UI, feature, or page into a reusable component — and share it across your applications. It’s easier to collaborate and build faster.

Learn more

Split apps into components to make app development easier, and enjoy the best experience for the workflows you want:

Micro-Frontends

Design System

Code-Sharing and reuse

Monorepo

--

--

JavaScript/Typescript experience with biggest corporates and scalable software, with focus on reactive / performance programming approach / high code quality.