序章

Angularは、HTTPサービスから返されたサブスクリプションのように、または非同期パイプを使用している場合に、監視可能なサブスクリプションからのサブスクリプション解除を処理します。 ただし、他の状況では、すべてのサブスクリプションを管理し、長期間有効なサブスクリプションから確実にサブスクリプションを解除することがすぐに困難になる可能性があります。 ほとんどのサブスクリプションからサブスクライブを解除するポリシーにも、独自の問題があります。

この記事では、手動でサブスクライブおよびサブスクライブ解除することに依存するAngularアプリケーションの例を紹介します。 次に、takeUntil演算子を使用してサブスクリプションを宣言的に管理するAngularアプリケーションの例と比較します。

前提条件

この記事をフォローしたい場合は、次のものが必要になります。

  • RxJSライブラリ、特にObservableSubscriptionにある程度精通していると有益です。
  • ApolloとGraphQLにある程度精通していると役立ちますが、必須ではありません。

このチュートリアルは、ノードv15.3.0、npm v6.14.9、@angular/core v11.0.4、rxjs v6.6.3、apollo-angular v2.1.0、 graph-tagv2.11.0。 この記事は、@angular/coreおよびrxjsの以前のバージョンからの移行における変更を反映するように編集されました。

手動で退会する

2つのサブスクリプションから手動でサブスクリプションを解除する例から始めましょう。

この例では、コードは Apollo watchQuery にサブスクライブして、GraphQLエンドポイントからデータを取得しています。

このコードは、onStartIntervalメソッドが呼び出されたときにサブスクライブする監視可能な間隔も作成しています。

import { Component, OnInit, OnDestroy } from '@angular/core';

import { Subscription, interval } from 'rxjs';

import { Apollo } from 'apollo-angular';
import gql from 'graphql-tag';

@Component({ ... })
export class AppComponent implements OnInit, OnDestroy {
  myQuerySubscription: Subscription;
  myIntervalSubscription: Subscription;

  constructor(private apollo: Apollo) {}

  ngOnInit() {
    this.myQuerySubscription = this.apollo.watchQuery<any>({
      query: gql`
        query getAllPosts {
          allPosts {
            title
            description
            publishedAt
          }
        }
      `
    })
    .valueChanges
    .subscribe(({data}) => {
      console.log(data);
    });
  }

  onStartInterval() {
    this.myIntervalSubscription = interval(250).subscribe(value => {
      console.log('Current value:', value);
    });
  }

  ngOnDestroy() {
    this.myQuerySubscription.unsubscribe();

    if (this.myIntervalSubscription) {
      this.myIntervalSubscription.unsubscribe();
    }
  }
}

ここで、コンポーネントに多くの同様のサブスクリプションがあると想像してください。コンポーネントが破棄されたときにすべてがサブスクライブ解除されるようにするためのかなりのプロセスになる可能性があります。

takeUntilで宣言的にサブスクライブを解除する

解決策は、takeUntil演算子を使用してサブスクリプションを作成し、ngOnDestroyライフサイクルフックで真の値を発行するサブジェクトを使用することです。

次のスニペットはまったく同じことを行いますが、今回はコードが宣言的にサブスクライブを解除します。 追加の利点は、サブスクリプションへの参照を保持する必要がなくなったことです。

import { Component, OnInit, OnDestroy } from '@angular/core';

import { Subject, interval } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

import { Apollo } from 'apollo-angular';
import gql from 'graphql-tag';

@Component({ ... })
export class AppComponent implements OnInit, OnDestroy {
  destroy$: Subject<boolean> = new Subject<boolean>();

  constructor(private apollo: Apollo) {}

  ngOnInit() {
    this.apollo.watchQuery<any>({
      query: gql`
        query getAllPosts {
          allPosts {
            title
            description
            publishedAt
          }
        }
      `
    })
    .valueChanges
    .pipe(takeUntil(this.destroy$))
    .subscribe(({data}) => {
      console.log(data);
    });
  }

  onStartInterval() {
    interval(250)
    .pipe(takeUntil(this.destroy$))
    .subscribe(value => {
      console.log('Current value:', value);
    });
  }

  ngOnDestroy() {
    this.destroy$.next(true);
    this.destroy$.unsubscribe();
  }
}

手動でサブスクライブを解除する代わりにtakeUntilのような演算子を使用すると、オブザーバブルも完了し、オブザーバブルで完了イベントがトリガーされることに注意してください。

コードをチェックして、意図しない副作用が発生しないことを確認してください。

結論

この記事では、takeUntilを使用して宣言的にサブスクライブを解除する方法について学習しました。 不要なサブスクリプションのサブスクリプションを解除すると、メモリリークの防止に役立ちます。 宣言的にサブスクリプションを解除すると、サブスクリプションへの参照を必要としないようになります。

taketakeWhilefirstなど、他にも同様のRxJS演算子があり、これらはすべて監視可能オブジェクトを完成させます。

Angularについて詳しく知りたい場合は、Angularトピックページで演習とプログラミングプロジェクトを確認してください。