React
该 @lit/react 包提供了实用程序,用于为 Web 组件创建 React 包装器组件,以及来自 响应式控制器 的自定义钩子。
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 组件。包装器允许您在组件上设置道具并向组件添加事件侦听器,就像您对任何其他 React 组件一样。useController()
允许您将 Lit 响应式控制器用作 React 钩子。
createComponent
“createComponent”的永久链接createComponent()
函数为自定义元素类创建 React 组件包装器。包装器会正确地将 React props
传递给自定义元素接受的属性,并监听自定义元素分发的事件。
导入 React
、自定义元素类和 createComponent
。
定义 React 组件后,您可以像使用任何其他 React 组件一样使用它。
在 React 游乐场示例 中查看实际效果。
createComponent
接受一个具有以下属性的选项对象
tagName
:自定义元素的标签名称。elementClass
:自定义元素类。react
:导入的React
对象。这用于使用用户提供的React
创建包装器组件。这也可以是preact-compat
的导入。events
:一个将事件处理程序道具映射到自定义元素触发的事件名称的对象。
使用插槽
“使用插槽”的永久链接使用 createComponent()
创建的组件的子元素将渲染到自定义元素的默认插槽。
要将子元素渲染到特定的命名插槽,可以添加标准的 slot
属性。
由于 React 组件本身不是 HTML 元素,因此它们通常不能直接具有 slot
属性。要渲染到命名插槽,组件需要用具有 slot
属性的容器元素包装。如果包装器元素会干扰样式,例如网格和 flexbox 布局,则为其提供 display: contents;
样式(有关详细信息,请参阅 MDN)将从渲染中删除容器,并且只渲染其子元素。
在 React 插槽游乐场示例 中试用一下。
events
选项接受一个将 React 道具名称映射到事件名称的对象。当组件用户传递一个具有其中一个事件道具名称的回调道具时,包装器将将其添加为相应事件的事件处理程序。
虽然 React 道具名称可以是您想要的任何名称,但推荐的约定是在事件名称前面添加 on
。这与 React 计划如何为自定义元素实现事件支持相匹配。您还应该确保此道具名称不会与元素上的任何现有属性发生冲突。
在 TypeScript 中,可以通过将事件名称强制转换为 EventName
实用程序类型来指定事件类型。这是一个良好的做法,这样 React 用户就可以为他们的事件回调获得最准确的类型。
EventName
类型是一个字符串,它接受一个事件接口作为类型参数。这里我们将 'my-event'
名称强制转换为 EventName<MyEvent>
以提供正确的事件类型
将事件名称强制转换为 EventName<MyEvent>
将导致 React 组件具有一个 onMyEvent
回调道具,该道具接受一个 MyEvent
参数,而不是一个普通的 Event
工作原理
“工作原理”的永久链接在渲染期间,包装器接收来自 React 的道具,并根据选项和自定义元素类,更改某些道具的行为
- 如果道具名称是自定义元素上的属性,如
in
检查所确定,则包装器将元素上的该属性设置为道具值 - 如果道具名称是传递给
events
选项的事件名称,则道具值将与事件名称一起传递给addEventListener()
。 - 否则,道具将传递给 React 的
createElement()
以将其渲染为属性。
属性和事件都在 componentDidMount()
和 componentDidUpdate()
回调中添加,因为元素必须已经由 React 实例化才能访问它。
对于事件,createComponent()
接受一个 React 事件道具名称到自定义元素触发的事件的映射。例如,传递 {onfoo: 'foo'}
表示当自定义元素触发名为 foo
的事件时,通过名为 onfoo
的道具传递的函数将被调用,并将事件作为参数。
useController
“useController”的永久链接响应式控制器允许开发人员挂钩到组件的生命周期,以将与功能相关的状态和行为捆绑在一起。它们在用例和功能上类似于 React 钩子,但它们是普通的 JavaScript 对象,而不是具有隐藏状态的函数。
useController()
允许您将响应式控制器变成 React 钩子,从而允许跨 Web 组件和 React 共享状态和行为。
在响应式控制器文档中查看 鼠标控制器示例,了解其实现。
工作原理
“工作原理”的永久链接useController()
为传递给它的控制器创建一个自定义主机对象,并通过使用 React 钩子来驱动控制器的生命周期。
useState()
用于存储控制器的实例和ReactControllerHost
- 钩子主体和
useLayoutEffect()
回调尽可能地模拟ReactiveElement
生命周期。 ReactControllerHost
实现addController()
,以便控制器组合正常工作,并且嵌套控制器生命周期被正确调用。ReactControllerHost
还通过调用useState()
设置器来实现requestUpdate()
,以便控制器可以使其主机组件重新渲染。