RXJS subscription management in Angular
If you’re reading this then you’re probably familiar with the fact that Angular uses RxJS and embraces the reactive paradigm. You might be familiar that RxJS is based on the Observable class. Let’s recap what this means.
To extract any information from an Observable we need to subscribe to it. A subscription is a bunch of optional callback functions. We can define a success, an error function and a completed function, all of them optional. When an Observable emits, RxJS invokes the proper callback function.
Subscriptions aren’t free though, you’re required to tell RxJS when you’re done with them. It’s pretty easy, you need to invoke their unsubscribe function. The problem is — when and how to do that?
Not unsubscribing
The easiest — and worst — solution is to ignore the problem. Subscribe to an Observable and leave it at that. When Angular destroys your component the open subscription will simply leak it.
Your subscriptions are holding a reference to the hosting component. If you don’t unsubscribe then you’ll create a memory leak with your whole component.
Manual subscription management
Let’s take a step in the right direction. Usually you’ll set up your observables in the ngOnInit lifecycle hook. There’s a corresponding hook when your component is destroyed, called ngOnDestroy. By simply storing all your subscriptions as members of your component class you can just call unsubscribe on all of them in the destroy hook.
This is a solution to the problem, but not a great one. For simple components it might work. But when you are adding more subscriptions later, you might forget to unsubscribe from them. And let’s face it, we just copying and pasting the code lines in cases like this, and will forget to change the variable name eventually.
Semi-automatic management with takeUntil
We can harness the power of hot observables, aka Subjects. The logic is the same as when you’re using manual management. The difference is that you create a subject. In every observable subscription you pipe your subject through the takeUntil operator.
You also create an ngOnDestroy hook and complete your subject there. This will unsubscribe from your observables.
There’s a slight catch though. Order matters when piping operators, and you generally want to put takeUntil to the very end. The actual reasons are beyond the scope of this post, refer to this article on Angular in Depth.
Using the async pipe
My personal favourite is to delegate the subscription management to Angular. This results in much less code and very straightforward components. Angular has the built-in async pipe for consuming observables. You’d use them in your component template.
Notice how all the subscription boilerplate code is gone. That’s a great thing, we need to maintain less code!
Also, when your component is destroyed Angular automatically unsubscribes from your observables. Though there are some pitfalls you should look out for.
If you use an async pipe multiple times on the same observable you’d get a different subscription. This could lead to multiple API calls. Fixing this will be the subject — pun intended — of my next post.
Source code
I’m maintaining and developing a repo on GitHub. It contains Angular7 best practices. Follow this link to the relevant part of the code.
View all posts by Gergely Koncz
Published November 16, 2018
Originally published at gergelykonczdotcom.wordpress.com on November 16, 2018.