Handling Manual Subscriptions
As we discussed in the last lesson, sometimes it isn’t feasible for us to use the async
pipe to handle subscribing/unsubscribing to a stream for us. This might be true when the “destination” for your data isn’t actually the template. Maybe you want to send a POST
request to send data to a server, and don’t need to display anything about that request in the template - in this case using the async
pipe probably doesn’t make sense.
Another situation that comes up a lot for people is reacting to the valueChanges
stream with ReactiveForms
. We have talked about this observable stream before - valueChanges
will emit every time the user modifies the value of the associated form control. An example of why you might need to subscribe
manually in a situation like this is:
ngOnInit(){
this.myForm
.get('fieldOne')
.valueChanges.pipe(
switchMap((val) => this.exampleService.getRelatedVal(val))
)
.subscribe((result) => {
this.myForm.get('fieldTwo').setValue(result)
})
}
What is happening here is that when fieldOne
changes, we want to take that value and make a call to a service to retrieve some related value. We then want to set that onto a different field in the form. You could consider some kind of auto-complete situation, filling in a value in one field might cause others to be filled based on that value.
In this case, we can’t really use the async
pipe, so a subscribe
here probably makes the most sense. But… if we subscribe
then we should also make sure we unsubscribe
at some point. How exactly should we do that?
How to Unsubscribe Manually
If you do subscribe manually at some point, it is important to understand how to unsubscribe safely. If you ever write .subscribe()
in your code you should always make sure you unsubscribe from that stream at some point. Technically, there are situations where you don’t have to unsubscribe - certain types of observables might just emit one value and then complete. If the complete()
notifier has been called, then the stream is already unsubscribed so there would be no need to do it manually.
This is a dangerous game to play, even in situations where it seems like it might be safe to not bother with unsubscribing there might be tricky little gotchas you aren’t considering. Rather than trying to reason about when you need to unsubscribe and when you don’t, it is much safer to always apply the general rule of: always unsubscribe.
The following are a few common techniques that you can use to handle this.
Store a reference to the subscription
This is the sort of “obvious” way to unsubscribe but I generally wouldn’t recommend doing this as other ways are generally more efficient. Still, I think it is important to cover since it sort of the default way to go about it.
export class HomeComponent implements OnInit, OnDestroy {
emitOnceASecondSubscription: Subscription;
ngOnInit(){
const emitOnceASecond$ = interval(1000);
this.emitOnceASecondSubscription = emitOnceASecond$.subscribe((val) => console.log(val));
}
ngOnDestroy(){
this.emitOnceASecondSubscription.unsubscribe();
}
}