Whenever I store the Subscription instances, I am using unsubscribe() during the ngOnDestroy life cycle in all components. But I want to implement that within one file, so I don't have to use unsubscribe() in each component.
As already said, takeUntilDestroyed is the most idiomatic way forward.
However, in my company we are using a base class for all components and services that need to handle unsubscribe on destroy. I still find it simpler, with less magic involved.
import { Injectable, OnDestroy } from '@angular/core';
import { Observable, ReplaySubject } from 'rxjs';
@Injectable()
export class AutoUnsubscribe implements OnDestroy {
readonly autoUnsubscriber: Observable<void> = new ReplaySubject<void>(1);
ngOnDestroy() {
if ((this.autoUnsubscriber as ReplaySubject<void>).closed) return;
(this.autoUnsubscriber as ReplaySubject<void>).next();
(this.autoUnsubscriber as ReplaySubject<void>).complete();
}
}
Then, you can make your components, directives or services inherit from AutoUnsubscribe
, and use everywhere the takeUntil
RxJS operator:
@Component({...})
export class MyComponent extends AutoUnsubscribe {
constructor(someService: MyService) {
super();
someService.getSome$().pipe(
takeUntil(this.autoUnsubscriber)
).subscribe(data => {
// ...
});
}
}
It just becomes second nature, and you don't need to define the ngOnDestroy method, which AFAICT you should with takeUntilDestroyed
.
Every developer needs to just learn to check that a takeUntil
is always the last operator in a pipe, just before the subscribe
.
This pattern removes also most of the benefits in using the async
pipe, that is very often addressed as a very good and clean way to handle subscription, but in my experience is very prone to multiple subscription problems (like multiple calls to the same api).