リアクティブコントローラー

リアクティブコントローラーは、コンポーネントのリアクティブ更新サイクルにフックできるオブジェクトです。コントローラーは、機能に関連する状態と動作をバンドルし、複数のコンポーネント定義間で再利用可能にします。

コントローラーを使用して、独自のステートとコンポーネントのライフサイクルへのアクセスを必要とする機能を実装できます。たとえば、

  • マウスイベントなどのグローバルイベントの処理
  • ネットワーク経由でのデータ取得などの非同期タスクの管理
  • アニメーションの実行

リアクティブコントローラーを使用すると、それ自体がコンポーネントではない小さな部品を組み合わせてコンポーネントを構築できます。それらは、独自のアイデンティティと状態を持つ、再利用可能な部分的なコンポーネント定義と考えることができます。

リアクティブコントローラーは、多くの点でクラスミックスインに似ています。主な違いは、独自のアイデンティティを持ち、コンポーネントのプロトタイプに追加しないことです。これにより、APIをカプセル化し、ホストコンポーネントごとに複数のコントローラーインスタンスを使用できます。コントローラーとミックスインの詳細を参照してください。

各コントローラーには独自の生成APIがありますが、通常はインスタンスを作成してコンポーネントに格納します。

コントローラーインスタンスに関連付けられたコンポーネントは、ホストコンポーネントと呼ばれます。

コントローラーインスタンスは、ホストコンポーネントからライフサイクルコールバックを受信し、コントローラーに新しいレンダリングデータがある場合にホストの更新をトリガーします。これは、ClockControllerの例で現在時刻が定期的にレンダリングされる仕組みです。

コントローラーは通常、ホストのrender()メソッドで使用される機能を公開します。たとえば、多くのコントローラーは、現在の値のような状態を持ちます。

各コントローラーには独自のAPIがあるため、使用方法については、特定のコントローラーのドキュメントを参照してください。

リアクティブコントローラーは、ホストコンポーネントに関連付けられたオブジェクトであり、1つ以上のホストライフサイクルコールバックを実装するか、ホストと対話します。さまざまな方法で実装できますが、初期化のためのコンストラクターとライフサイクルのためのメソッドを使用してJavaScriptクラスを使用することに重点を置きます。

コントローラーは、host.addController(this)を呼び出すことで、ホストコンポーネントに自身を登録します。通常、コントローラーは後で対話できるように、ホストコンポーネントへの参照を格納します。

1回限りの設定のために、他のコンストラクターパラメーターを追加できます。

コントローラーがホストコンポーネントに登録されると、ライフサイクルコールバックやその他のクラスフィールドとメソッドをコントローラーに追加して、目的の状態と動作を実装できます。

ReactiveControllerインターフェースで定義されているリアクティブコントローラーのライフサイクルは、リアクティブ更新サイクルのサブセットです。LitElementは、そのライフサイクルコールバック中にインストールされているコントローラーに呼び出します。これらのコールバックはオプションです。

  • hostConnected():
    • ホストが接続されたときに呼び出されます。
    • renderRootの作成後に呼び出されるため、この時点でシャドウルートが存在します。
    • イベントリスナー、オブザーバーなどの設定に役立ちます。
  • hostUpdate():
    • ホストのupdate()メソッドとrender()メソッドの前に呼び出されます。
    • 更新前にDOMを読み取るのに役立ちます(たとえば、アニメーションの場合)。
  • hostUpdated():
    • 更新後、ホストのupdated()メソッドの前に呼び出されます。
    • DOMが変更された後、DOMを読み取るのに役立ちます(たとえば、アニメーションの場合)。
  • hostDisconnected():
    • ホストが切断されたときに呼び出されます。
    • hostConnected()で追加されたもの(イベントリスナーやオブザーバーなど)の後処理に役立ちます。

詳細については、リアクティブ更新サイクルを参照してください。

リアクティブコントローラーホストは、コントローラーの追加と更新の要求のための小さなAPIを実装し、コントローラーのライフサイクルメソッドを呼び出す責任があります。

これは、コントローラーホストで公開される最小限のAPIです。

  • addController(controller: ReactiveController)
  • removeController(controller: ReactiveController)
  • requestUpdate()
  • updateComplete: Promise<boolean>

HTMLElementReactiveElementLitElementに固有のコントローラーを作成し、これらのAPIをさらに必要とするコントローラー、または特定の要素クラスまたはその他のインターフェースに関連付けられたコントローラーを作成することもできます。

LitElementReactiveElementはコントローラーホストですが、ホストは他のWebコンポーネントライブラリの基本クラス、フレームワークのコンポーネント、またはその他のコントローラーなどの他のオブジェクトでもあります。

コントローラーは、他のコントローラーで構成することもできます。これを行うには、子コントローラーを作成し、ホストをそれに転送します。

コントローラーとディレクティブを組み合わせることは、特にレンダリングの前後に行う必要がある作業を行うディレクティブ(アニメーションディレクティブなど)、またはテンプレート内の特定の要素への参照が必要なコントローラーにとって非常に強力なテクニックです。

コントローラーとディレクティブを使用する主なパターンは2つあります。

  • コントローラーディレクティブ。これらは、ホストのライフサイクルにフックするために、それ自体がコントローラーであるディレクティブです。
  • ディレクティブを所有するコントローラー。これらは、ホストのテンプレートで使用するための1つ以上のディレクティブを作成するコントローラーです。

ディレクティブの作成の詳細については、カスタムディレクティブを参照してください。

リアクティブコントローラーは、ホストのインスタンスフィールドとして格納する必要はありません。addController()を使用してホストに追加されたものはすべてコントローラーです。特に、ディレクティブもコントローラーになることができます。これにより、ディレクティブはホストのライフサイクルにフックできます。

ディレクティブはスタンドアロン関数である必要はなく、コントローラーなどの他のオブジェクトのメソッドでもあります。これは、コントローラーがテンプレート内の特定の要素への参照を必要とする場合に役立ちます。

たとえば、ResizeObserverを使用して要素のサイズを観察できるResizeControllerを考えてみましょう。動作するには、ResizeControllerインスタンスと、観察する要素に配置されるディレクティブの両方が必要です。

これを実装するには、ディレクティブを作成してメソッドから呼び出します。

TODO

  • この例を見直して整理します。

リアクティブコントローラーは非常に一般的であり、非常に幅広いユースケースがあります。ユーザー入力、状態管理、またはリモートAPIなどの外部リソースにコンポーネントを接続する場合に特に適しています。いくつかの一般的なユースケースを次に示します。

リアクティブコントローラーを使用して、外部入力に接続できます。たとえば、キーボードとマウスのイベント、サイズオブザーバー、またはミューテーションオブザーバーなどです。コントローラーは、レンダリングで使用するための入力の現在の値を提供し、値が変更されたときにホストの更新を要求できます。

この例は、コントローラーがホストが接続および切断されたときにセットアップとクリーンアップ作業を実行し、入力が変更されたときに更新を要求する方法を示しています。

長時間実行される計算やネットワークI/Oなどの非同期タスクは、通常、時間が経つにつれて変化する状態を持ち、タスクの状態が変更された(完了、エラーなど)ときにホストに通知する必要があります。

コントローラーは、タスク実行と状態をバンドルして、コンポーネント内で簡単に使用できるようにする優れた方法です。コントローラーとして記述されたタスクは、通常、ホストが設定できる入力と、ホストがレンダリングできる出力を持ちます。

@lit/taskには、ホストから入力を取得し、タスク関数を実行し、タスクの状態に応じて異なるテンプレートをレンダリングできる汎用的なTaskコントローラーが含まれています。

Taskを使用して、特定のタスクに合わせて調整されたAPIを持つカスタムコントローラーを作成できます。ここでは、デモREST APIから指定された名前のリストのいずれかをフェッチできるNamesControllerTaskをラップします。NameControllerは入力としてkindプロパティを公開し、タスクの状態に応じて4つのテンプレートのいずれかをレンダリングできるrender()メソッドを公開します。タスクロジックとそのホストの更新方法は、ホストコンポーネントから抽象化されています。

TODO

  • アニメーション