React
@lit/react 套件提供了實用工具,可為 Web 元件建立 React 包裝元件,並從響應式控制器建立自訂 Hook。
React 元件包裝器可讓您在自訂元素上設定屬性(而不僅僅是屬性)、將 DOM 事件對應到 React 風格的回呼,並在 TypeScript 中啟用 JSX 的正確型別檢查。
這些包裝器針對兩個不同的受眾
- Web 元件的使用者可以包裝元件和控制器,以便在自己的 React 專案中使用。
- 元件的供應商可以發佈 React 包裝器,以便他們的 React 使用者擁有其元件的慣用版本。
為什麼需要包裝器?
連結至「為什麼需要包裝器?」React 已經可以渲染 Web 元件,因為自訂元素只是 HTML 元素,而且 React 知道如何渲染 HTML。但是 React 對於 HTML 元素做出了一些假設,這些假設並不總是適用於自訂元素,而且它以不同的方式對待小寫標籤名稱與大寫元件名稱,這可能會使自訂元素的使用比必要的更困難。
例如,React 假設所有 JSX 屬性都對應到 HTML 元素屬性,並且沒有提供設定屬性的方法。這使得很難將複雜的資料(如物件、陣列或函式)傳遞給 Web 元件。React 也假設所有 DOM 事件都有對應的「事件屬性」(onclick
、onmousemove
等),並使用它們而不是呼叫 addEventListener()
。這表示為了正確使用更複雜的 Web 元件,您通常必須使用 ref()
和命令式程式碼。(如需關於 React Web 元件整合限制的更多資訊,請參閱Custom Elements Everywhere。)
React 正在努力解決這些問題,但在此期間,我們的包裝器會負責為您設定屬性和監聽事件。
@lit/react
套件提供了兩個主要的匯出
createComponent()
會建立一個 React 元件,該元件會「包裝」現有的 Web 元件。包裝器可讓您在元件上設定 Props,並將事件監聽器新增至元件,就像您對待任何其他 React 元件一樣。useController()
可讓您將 Lit 響應式控制器當作 React Hook 使用。
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<MyEvent>
,以提供正確的事件型別
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<MyEvent>
會使 React 元件具有一個接受 MyEvent
參數(而不是純 Event
)的 onMyEvent
回呼屬性
<MyElementComponent
onmy-event={(e: MyEvent) => {
console.log(e.myEventData);
}}
/>
運作方式
連結至「運作方式」在渲染期間,包裝器會從 React 接收 Props,並根據選項和自訂元素類別,變更某些 Props 的行為
- 如果屬性名稱是自訂元素上的屬性(使用
in
檢查判斷),則包裝器會將元素上的該屬性設定為 Props 值 - 如果屬性名稱是傳遞給
events
選項的事件名稱,則會將屬性值傳遞給addEventListener()
,並使用該事件的名稱。 - 否則,該屬性會傳遞給 React 的
createElement()
以渲染為屬性。
屬性和事件都會在 componentDidMount()
和 componentDidUpdate()
回呼中新增,因為元素必須已經由 React 實例化,才能存取它。
對於事件,createComponent()
接受一個 React 事件屬性名稱到自訂元素觸發的事件的對應。例如,傳遞 {onfoo: 'foo'}
表示當自訂元素觸發 foo
事件時,會呼叫透過名為 onfoo
的屬性傳遞的函式,並將事件當作引數傳遞。
useController
連結至「useController」響應式控制器允許開發人員掛鉤到元件的生命週期,以將與功能相關的狀態和行為捆綁在一起。它們在使用者案例和功能方面類似於 React Hook,但是是純 JavaScript 物件,而不是具有隱藏狀態的函式。
useController()
可讓您從響應式控制器建立 React Hook,以便在 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 Hook 來驅動控制器的生命週期。
useState()
用於儲存控制器的實例和ReactControllerHost
- Hook 主體和
useLayoutEffect()
回呼會盡可能地模擬ReactiveElement
的生命週期。 ReactControllerHost
會實作addController()
,以便控制器組合有效,並且會正確呼叫巢狀控制器的生命週期。ReactControllerHost
也會透過呼叫useState()
設定器來實作requestUpdate()
,以便控制器可以導致其主機元件重新渲染。