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イベントに対応する「イベントプロパティ」(onclick
、onmousemove
など)があると想定し、addEventListener()
を呼び出す代わりにそれらを使用します。これは、より複雑なWebコンポーネントを適切に使用するには、多くの場合、ref()
と命令型コードを使用する必要があることを意味します。(ReactのWebコンポーネント統合の制限事項の詳細については、Custom Elements Everywhereを参照してください。)
Reactはこれらの問題の修正に取り組んでいますが、その間、ラッパーはプロパティの設定とイベントのリッスンを処理します。
@lit/react
パッケージは、主に2つのエクスポートを提供します
createComponent()
は、既存のWebコンポーネントを*ラップ*するReactコンポーネントを作成します。ラッパーを使用すると、他のReactコンポーネントと同様に、コンポーネントに小道具を設定し、コンポーネントにイベントリスナーを追加できます。useController()
を使用すると、LitリアクティブコントローラーをReactフックとして使用できます。
createComponent
「createComponent」へのパーマリンクcreateComponent()
関数は、カスタム要素クラスのReactコンポーネントラッパーを作成します。ラッパーは、Reactのprops
をカスタム要素が受け入れるプロパティに正しく渡し、カスタム要素によってディスパッチされたイベントをリッスンします。
使用方法
「使用方法」へのパーマリンクReact
、カスタム要素クラス、およびcreateComponent
をインポートします。
import React from 'react';
import {createComponent} from '@lit/react';
import {MyElement} from './my-element.js';
export const MyElementComponent = createComponent({
tagName: 'my-element',
elementClass: MyElement,
react: React,
events: {
onactivate: 'activate',
onchange: 'change',
},
});
Reactコンポーネントを定義した後、他のReactコンポーネントと同様に使用できます。
<MyElementComponent
active={isActive}
onactivate={(e) => setIsActive(e.active)}
onchange={handleChange}
/>
Reactプレイグラウンドの例で実際に確認してください。
オプション
「オプション」へのパーマリンクcreateComponent
は、次のプロパティを持つオプションオブジェクトを取ります
tagName
: カスタム要素のタグ名。elementClass
: カスタム要素クラス。react
: インポートされたReact
オブジェクト。これは、ユーザーが提供したReact
でラッパーコンポーネントを作成するために使用されます。これは、preact-compat
のインポートでもかまいません。events
: イベントハンドラプロパティをカスタム要素によって発生したイベント名にマッピングするオブジェクト。
スロットの使用
「スロットの使用」へのパーマリンクcreateComponent()
で作成されたコンポーネントの子は、カスタム要素のデフォルトスロットにレンダリングされます。
<MyElementComponent>
<p>This will render in the default slot.</p>
</MyElementComponent>
子を特定の名前付きスロットにレンダリングするには、標準のslot
属性を追加できます。
<MyElementComponent>
<p slot="foo">This will render in the slot named "foo".</p>
</MyElementComponent>
Reactコンポーネント自体はHTML要素ではないため、通常は直接slot
属性を持つことはできません。名前付きスロットにレンダリングするには、コンポーネントをslot
属性を持つコンテナ要素でラップする必要があります。ラッパー要素がグリッドやフレックスボックスレイアウトなどのスタイルに干渉する場合は、display: contents;
スタイルを指定すると(詳細についてはMDNを参照)、コンテナがレンダリングから削除され、子のみがレンダリングされます。
<MyElementComponent>
<div slot="foo" style="display: contents;">
<ReactComponent />
</div>
</MyElementComponent>
Reactスロットプレイグラウンドの例で試してみてください。
イベント
「イベント」へのパーマリンクevents
オプションは、Reactプロパティ名をイベント名にマッピングするオブジェクトを取ります。コンポーネントユーザーがイベントプロパティ名のいずれかを持つコールバックプロパティを渡すと、ラッパーはそれを対応するイベントのイベントハンドラとして追加します。
Reactプロパティ名は好きなように設定できますが、推奨される規則は、イベント名の前にon
を追加することです。これは、Reactがカスタム要素のイベントサポートを実装する方法と一致します。また、このプロパティ名が要素の既存のプロパティと競合しないようにする必要があります。
TypeScriptでは、イベント名を`EventName`ユーティリティ型にキャストすることで、イベント型を指定できます。これは、Reactユーザーがイベントコールバックに最も正確な型を取得できるようにするための良い習慣です。
`EventName`型は、イベントインターフェースを型パラメータとして取る文字列です。ここでは、正しいイベント型を提供するために、 ''my-event'' という名前を`EventName
import React from 'react';
import {createComponent, type EventName} from '@lit/react';
import {MyElement, MyEvent} from './my-element.js';
export const MyElementComponent = createComponent({
tagName: 'my-element',
elementClass: MyElement,
react: React,
events: {
'onmy-event': 'my-event' as EventName<MyEvent>,
},
});
イベント名を`EventName
<MyElementComponent
onmy-event={(e: MyEvent) => {
console.log(e.myEventData);
}}
/>
レンダリング中、ラッパーはReactからpropsを受け取り、オプションとカスタム要素クラスに基づいて、一部のpropsの動作を変更します
- `in`チェックで判断されるように、プロパティ名がカスタム要素のプロパティである場合、ラッパーはその要素のプロパティをプロパティ値に設定します
- プロパティ名が`events`オプションに渡されたイベント名である場合、プロパティ値はイベントの名前とともに`addEventListener()`に渡されます。
- それ以外の場合、propはReactの`createElement()`に渡されて属性としてレンダリングされます。
プロパティとイベントはどちらも`componentDidMount()`と`componentDidUpdate()`コールバックで追加されます。これは、要素にアクセスするには、Reactによってすでにインスタンス化されている必要があるためです。
イベントの場合、`createComponent()`は、Reactイベントプロパティ名とカスタム要素によって発生したイベントのマッピングを受け入れます。たとえば、`{onfoo: 'foo'}`を渡すと、`onfoo`という名前のプロパティを介して渡された関数は、カスタム要素がイベントを引数として`foo`イベントを発生させたときに呼び出されます。
useController
「useController」へのパーマリンクリアクティブコントローラーを使用すると、開発者はコンポーネントのライフサイクルにフックして、機能に関連する状態と動作をまとめてバンドルできます。ユーザーケースと機能はReactフックに似ていますが、隠れた状態を持つ関数ではなく、プレーンなJavaScriptオブジェクトです.
`useController()`を使用すると、リアクティブコントローラーからReactフックを作成できるため、WebコンポーネントとReactで状態と動作を共有できます。
使用方法
「使用方法」へのパーマリンクimport React from 'react';
import {useController} from '@lit/react/use-controller.js';
import {MouseController} from '@example/mouse-controller';
// Write a custom React hook function:
const useMouse = () => {
// Use useController to create and store a controller instance:
const controller = useController(React, (host) => new MouseController(host));
// Return relevant data for consumption by the component:
return controller.pos;
};
// Now use the new hook in a React component:
const Component = (props) => {
const mousePosition = useMouse();
return (
<pre>
x: {mousePosition.x}
y: {mousePosition.y}
</pre>
);
};
実装については、リアクティブコントローラードキュメントのマウスコントローラーの例を参照してください。
`useController()`は、渡されたコントローラーのカスタムホストオブジェクトを作成し、Reactフックを使用してコントローラーのライフサイクルを駆動します。
- `useState()`は、コントローラーのインスタンスと`ReactControllerHost`を格納するために使用されます
- フック本体と`useLayoutEffect()`コールバックは、`ReactiveElement`ライフサイクルを可能な限り厳密にエミュレートします。
- `ReactControllerHost`は`addController()`を実装しているため、コントローラーのコンポジションが機能し、ネストされたコントローラーのライフサイクルが正しく呼び出されます。
- `ReactControllerHost`は、`useState()`セッターを呼び出すことによって`requestUpdate()`も実装しているため、コントローラーはホストコンポーネントを再レンダリングさせることができます.