React

@lit/react パッケージは、Webコンポーネント用のReactラッパーコンポーネントを作成するためのユーティリティと、リアクティブコントローラーからのカスタムフックを提供します。

Reactコンポーネントラッパーを使用すると、カスタム要素に(属性だけでなく)プロパティを設定し、DOMイベントをReactスタイルのコールバックにマッピングし、TypeScriptによるJSXでの正しい型チェックを可能にします。

ラッパーは2つの異なる対象ユーザーを対象としています

  • Webコンポーネントのユーザーは、自分のReactプロジェクトで使用するコンポーネントとコントローラーをラップできます。
  • コンポーネントのベンダーは、Reactユーザーがコンポーネントの慣用的なバージョンを使用できるように、Reactラッパーを公開できます。

ReactはすでにWebコンポーネントをレンダリングできます。カスタム要素はHTML要素であり、ReactはHTMLのレンダリング方法を知っているためです。ただし、ReactはHTML要素について、カスタム要素には必ずしも当てはまらない前提を置いており、小文字のタグ名と大文字のコンポーネント名を区別して扱うため、カスタム要素を使用するのが unnecessarily 難しくなる可能性があります。

たとえば、ReactはすべてのJSXプロパティがHTML要素の属性にマッピングされると想定しており、プロパティを設定する方法を提供していません。そのため、Webコンポーネントに複雑なデータ(オブジェクト、配列、関数など)を渡すのが難しくなります。Reactはまた、すべてのDOMイベントに対応する「イベントプロパティ」(onclickonmousemoveなど)があると想定し、addEventListener()を呼び出す代わりにそれらを使用します。これは、より複雑なWebコンポーネントを適切に使用するには、多くの場合、ref()と命令型コードを使用する必要があることを意味します。(ReactのWebコンポーネント統合の制限事項の詳細については、Custom Elements Everywhereを参照してください。)

Reactはこれらの問題の修正に取り組んでいますが、その間、ラッパーはプロパティの設定とイベントのリッスンを処理します。

@lit/reactパッケージは、主に2つのエクスポートを提供します

  • createComponent()は、既存のWebコンポーネントを*ラップ*するReactコンポーネントを作成します。ラッパーを使用すると、他のReactコンポーネントと同様に、コンポーネントに小道具を設定し、コンポーネントにイベントリスナーを追加できます。

  • useController()を使用すると、LitリアクティブコントローラーをReactフックとして使用できます。

createComponent()関数は、カスタム要素クラスのReactコンポーネントラッパーを作成します。ラッパーは、Reactのpropsをカスタム要素が受け入れるプロパティに正しく渡し、カスタム要素によってディスパッチされたイベントをリッスンします。

React、カスタム要素クラス、およびcreateComponentをインポートします。

Reactコンポーネントを定義した後、他のReactコンポーネントと同様に使用できます。

Reactプレイグラウンドの例で実際に確認してください。

createComponentは、次のプロパティを持つオプションオブジェクトを取ります

  • tagName: カスタム要素のタグ名。
  • elementClass: カスタム要素クラス。
  • react: インポートされたReactオブジェクト。これは、ユーザーが提供したReactでラッパーコンポーネントを作成するために使用されます。これは、preact-compatのインポートでもかまいません。
  • events: イベントハンドラプロパティをカスタム要素によって発生したイベント名にマッピングするオブジェクト。

createComponent()で作成されたコンポーネントの子は、カスタム要素のデフォルトスロットにレンダリングされます。

子を特定の名前付きスロットにレンダリングするには、標準のslot属性を追加できます。

Reactコンポーネント自体はHTML要素ではないため、通常は直接slot属性を持つことはできません。名前付きスロットにレンダリングするには、コンポーネントをslot属性を持つコンテナ要素でラップする必要があります。ラッパー要素がグリッドやフレックスボックスレイアウトなどのスタイルに干渉する場合は、display: contents;スタイルを指定すると(詳細についてはMDNを参照)、コンテナがレンダリングから削除され、子のみがレンダリングされます。

Reactスロットプレイグラウンドの例で試してみてください。

eventsオプションは、Reactプロパティ名をイベント名にマッピングするオブジェクトを取ります。コンポーネントユーザーがイベントプロパティ名のいずれかを持つコールバックプロパティを渡すと、ラッパーはそれを対応するイベントのイベントハンドラとして追加します。

Reactプロパティ名は好きなように設定できますが、推奨される規則は、イベント名の前にonを追加することです。これは、Reactがカスタム要素のイベントサポートを実装する方法と一致します。また、このプロパティ名が要素の既存のプロパティと競合しないようにする必要があります。

TypeScriptでは、イベント名を`EventName`ユーティリティ型にキャストすることで、イベント型を指定できます。これは、Reactユーザーがイベントコールバックに最も正確な型を取得できるようにするための良い習慣です。

`EventName`型は、イベントインターフェースを型パラメータとして取る文字列です。ここでは、正しいイベント型を提供するために、 ''my-event'' という名前を`EventName`にキャストします。

イベント名を`EventName`にキャストすると、Reactコンポーネントは、プレーン`Event`ではなく`MyEvent`パラメータを受け入れる`onMyEvent`コールバックプロパティを持つようになります。

レンダリング中、ラッパーはReactからpropsを受け取り、オプションとカスタム要素クラスに基づいて、一部のpropsの動作を変更します

  • `in`チェックで判断されるように、プロパティ名がカスタム要素のプロパティである場合、ラッパーはその要素のプロパティをプロパティ値に設定します
  • プロパティ名が`events`オプションに渡されたイベント名である場合、プロパティ値はイベントの名前とともに`addEventListener()`に渡されます。
  • それ以外の場合、propはReactの`createElement()`に渡されて属性としてレンダリングされます。

プロパティとイベントはどちらも`componentDidMount()`と`componentDidUpdate()`コールバックで追加されます。これは、要素にアクセスするには、Reactによってすでにインスタンス化されている必要があるためです。

イベントの場合、`createComponent()`は、Reactイベントプロパティ名とカスタム要素によって発生したイベントのマッピングを受け入れます。たとえば、`{onfoo: 'foo'}`を渡すと、`onfoo`という名前のプロパティを介して渡された関数は、カスタム要素がイベントを引数として`foo`イベントを発生させたときに呼び出されます。

リアクティブコントローラーを使用すると、開発者はコンポーネントのライフサイクルにフックして、機能に関連する状態と動作をまとめてバンドルできます。ユーザーケースと機能はReactフックに似ていますが、隠れた状態を持つ関数ではなく、プレーンなJavaScriptオブジェクトです.

`useController()`を使用すると、リアクティブコントローラーからReactフックを作成できるため、WebコンポーネントとReactで状態と動作を共有できます。

実装については、リアクティブコントローラードキュメントのマウスコントローラーの例を参照してください。

`useController()`は、渡されたコントローラーのカスタムホストオブジェクトを作成し、Reactフックを使用してコントローラーのライフサイクルを駆動します。

  • `useState()`は、コントローラーのインスタンスと`ReactControllerHost`を格納するために使用されます
  • フック本体と`useLayoutEffect()`コールバックは、`ReactiveElement`ライフサイクルを可能な限り厳密にエミュレートします。
  • `ReactControllerHost`は`addController()`を実装しているため、コントローラーのコンポジションが機能し、ネストされたコントローラーのライフサイクルが正しく呼び出されます。
  • `ReactControllerHost`は、`useState()`セッターを呼び出すことによって`requestUpdate()`も実装しているため、コントローラーはホストコンポーネントを再レンダリングさせることができます.