Unsubscribe on ngOnDestroy hook
Advantages:
- simplicity,
- can unsubscribe individually.
Disadvantages:
- boilerplate code,
- a potential problem with the initial value (not null safety),
- need to manually unsubscribe.
@Component({})
export class MyComponent implements OnInit, OnDestroy {
private sub1!: Subscription;
private sub2!: Subscription;
ngOnInit(): void {
this.sub1 = interval(1000).subscribe();
this.sub2 = interval(1000).subscribe();
}
ngOnDestroy(): void {
this.sub1.unsubscribe();
this.sub2.unsubscribe();
}
}
Unsubscribe on ngOnDestroy hook with subscriptions container
Advantages:
- simplicity,
- null safety,
- need to unsubscribe always only once.
Disadvantages:
- boilerplate code,
- need to manually unsubscribe.
@Component({})
export class MyComponent implements OnInit, OnDestroy {
private subs = new Subscription();
ngOnInit(): void {
const sub1 = interval(1000).subscribe();
const sub2 = interval(1000).subscribe();
this.subs.add(sub1);
this.subs.add(sub2);
}
ngOnDestroy(): void {
this.subs.unsubscribe();
}
}
Unsubscribe with async pipe
Advantages:
- no need to implement OnInit and OnDestroy interfaces, async pipe does it automatically,
- independent of change detection strategy if data from a stream are used within a template.
Disadvantages:
- less performance (async is not a pure pipe) with comparison to subscribe in component logic.
@Component({
template: `
<ng-container *ngIf="interval1$ | async"></ng-container>
<ng-container *ngIf="interval2$ | async"></ng-container>
`,
})
export class MyComponent {
interval1$ = interval(1000);
interval2$ = interval(1000);
}
Complete stream with takeUnitl operator
Advantages:
- simplicity,
- null safety,
- calling next on destroy$ causes complete on all streams.
Disadvantages:
- need to manually unsubscribe.
@Component({})
export class MyComponent implements OnInit, OnDestroy {
private destroy$ = new Subject<void>();
ngOnInit(): void {
interval(1000).pipe(takeUntil(this.destroy$)).subscribe();
interval(1000).pipe(takeUntil(this.destroy$)).subscribe();
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
}
Unsubscribe/Complete with destroyable local service
Thanks to destroyable local service you can move unsubscribe logic outside the component.
Advantages:
- automatically unsubscribe, no need to implement OnInit and OnDestroy interfaces,
- unsubscribe logic is handled in one place,
- the single-responsibility principle is preserved,
- BONUS: you can handle unsubscribe and takeUntil pattern in one place
Disadvantages:
- the component has an additional provider.
@Injectable()
export class Unsubscriber extends Subject<void> implements OnDestroy {
private subs = new Subscription();
set add(sub: Subscription) {
this.subs.add(sub);
}
ngOnDestroy(): void {
this.next();
this.complete();
this.subs.unsubscribe();
}
}
@Component({
providers: [Unsubscriber],
})
export class MyComponent implements OnInit {
constructor(private unsubscriber: Unsubscriber) {}
ngOnInit(): void {
// with takeUntil pattern
interval(1000).pipe(takeUntil(this.unsubscriber)).subscribe();
// with unsubscribe pattern
this.unsubscriber.add = interval(1000).subscribe();
}
}