Angular subscribe elegant

My favourite unsubscribe pattern is to use takeUntil. takeUntil() accepts an observable, and will stop the subscription when that observable emits a value.

The long-winded way to do this is emit a value in ngOnDestroy().

destroyed = new Subject<void>();

ngOnInit(): void {
  myService.getData().pipe(
    takeUntil(this.destroyed)
  ).subscribe(data => {
    // process data
  });
}

ngOnDestroy(): void {
  this.destroyed.next();
  this.destroyed.complete();
}

This gets quite tedious once you’ve done this a few times. The best solution I’ve seen for this (and currently use) is to set up a shared function that emits the destroyed value for you. Source Credit

import { OnDestroy } from '@angular/core';

import { ReplaySubject, Subject, Observable } from 'rxjs';

export function componentDestroyed(component: OnDestroy): Observable<void> {
  const oldNgOnDestroy: () => void = component.ngOnDestroy;
  const destroyed: Subject<void> = new ReplaySubject<void>(1);
  component.ngOnDestroy = () => {
    oldNgOnDestroy.apply(component);
    destroyed.next(undefined);
    destroyed.complete();
  };
  return destroyed.asObservable();
}

It is then almost trivial to use in your component:

ngOnInit(): void {
  myService.getData().pipe(
    takeUntil(componentDestroyed(this))
  ).subscribe(data => {
    // process data
  });
}

ngOnDestroy(): void {}

All you have to do is implement ngOnDestroy in your component and add takeUntil(componentDestroyed(this)) to your pipe.

Leave a Comment