EmotionTechテックブログ

株式会社エモーションテックのProduct Teamのメンバーが、日々の取り組みや技術的なことを発信していくブログです。

Angular に新しく追加された takeUntilDestroyed API について

こんにちはあるいはこんばんは。フロントエンドエンジニアの id:kasaharu です。 エモーションテックでは Angular を使ったフロントエンド開発をしています。

ちょうど 1 年ほど前に Angular に関する記事を書きました。

NgRx ComponentStore の lifecycle hook を使って Subscription を購読解除する - EmotionTechテックブログ

Angular のアップデートに伴い、この記事でやっていたことが Angular の core が提供する API でできるようになったのでその紹介をします。

この記事は エモーションテック Advent Calendar 2023 の 18 日目の記事です。

はじめに

Angular v17 で新しい reactive system として Signal が提供開始されましたが、既存のアプリケーションにはまだ RxJS を使うコードが多く残っていることでしょう。

今回はコンポーネント内の Subscription をコンポーネント破棄時に購読解除する新しい API として takeUntilDestroyed を紹介します。

takeUntilDestroyed が実装される前の購読解除の例

去年の記事では NgRx ComponentStore の API を使う方法を紹介しました。

しかしこのライブラリは人気があるとはいえ 3rd party のライブラリです。もちろん導入していない場合もあるでしょう。 導入していなかった場合、destroy 用のストリームを用意するのが一般的です。

例を見てみます。1 秒ごとに流れるストリームをコンポーネント破棄時に購読解除するとしたら、下記のようになります。

@Component({...})
export class MyComponent implements OnDestroy {
  numbers = interval(1000);
  private onDestroy$ = new Subject<void>();

  constructor() {
    this.numbers.pipe(takeUntil(this.onDestroy$)).subscribe(console.log);
  }

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

やはり、購読解除のために ngOnDestroy を実装する必要があります。新しく追加された takeUntilDestroyed を使うどうなるでしょうか?

takeUntilDestroyed を使った購読解除

さっそく takeUntilDestroyed で書き直してみます。takeUntilDestroyed@angular/core/rxjs-interop から提供されています。

@Component({...})
export class MyComponent {
  numbers = interval(1000);

  constructor() {
    this.numbers.pipe(takeUntilDestroyed()).subscribe(console.log);
  }
}

かなりスッキリしました!

注意点としてこの API をインジェクションコンテキスト外から使用する場合は引数に DestroyRef を渡す必要があります。 インジェクションコンテキストはそのクラスのフィールド初期化時、または constructor 内になります。

つまり、今 constructor で処理している内容を ngOnInit でおこなうと動かなくなります。

@Component({...})
export class MyComponent implements OnInit {
  numbers = interval(1000);

  ngOnInit() {
    this.numbers.pipe(takeUntilDestroyed()).subscribe(console.log);
  }
}

このとき NG0203 のエラーが出ているはずです。NG0203 が出たときは、落ち着いて DestroyRef を引数で渡してあげましょう。

@Component({...})
export class MyComponent implements OnInit {
  #destroyRef = inject(DestroyRef);
  numbers = interval(1000);

  ngOnInit() {
    this.numbers.pipe(takeUntilDestroyed(this.#destroyRef)).subscribe(console.log);
  }
}

これでエラーは解消します。簡単です。使ってみたくなったのではないでしょうか?

takeUntilDestroyed は現在 developer preview です。developer previewAPI は基本的には用法用量を守って使うことをお勧めします。

しかし、このくらいなら何か大きな破壊的変更が入ってもすぐに元に戻せることと、この API を使うことでボイラープレートがかなり減るため、チームでもさっそく使い始めています。

まとめ

いかがでしたか?これも、現在の大きな目標のひとつに開発体験の向上を掲げる Angular らしいアップデートなのかもしれません。 明日は et-yoshikawa さんの記事です、お楽しみに!

We're Hiring!

エモーションテックでは顧客体験、従業員体験の改善をサポートし、世の中の体験を変えるプロダクトを開発しています。プロダクトに興味のある方、Angular を使ったアプリケーション開発をしたい方、ぜひ採用ページからご応募をお願いいたします。

hrmos.co