¥Composition
使用 asChild
属性将 Radix 的功能组合到其他元素类型或你自己的 React 组件上。
所有渲染 DOM 元素的 Radix 基元部分都接受 asChild
prop。当 asChild
设置为 true
时,Radix 不会渲染默认 DOM 元素,而是克隆该部件的子元素,并传递使其功能齐全所需的 props 和行为。
¥All Radix primitive parts that render a DOM element accept an asChild
prop. When asChild
is set to true
, Radix will not render a default DOM element, instead cloning the part's child and passing it the props and behavior required to make it functional.
¥Changing the element type
在大多数情况下,你无需修改元素类型,因为 Radix 的设计旨在提供最合适的默认值。然而,在某些情况下,这样做是有益的。
¥In the majority of cases you shouldn’t need to modify the element type as Radix has been designed to provide the most appropriate defaults. However, there are cases where it is helpful to do so.
Tooltip.Trigger
就是一个很好的例子。默认情况下,此部分呈现为 button
,但你可能也希望为链接(a
标签)添加工具提示。让我们看看如何使用 asChild
实现这一点:
¥A good example is with Tooltip.Trigger
. By default this part is rendered as a button
, though you may want to add a tooltip to a link (a
tag) as well. Let's see how you can achieve this using asChild
:
如果你决定更改底层元素类型,你有责任确保其保持可访问性和功能性。例如,对于
Tooltip.Trigger
,它必须是一个可聚焦元素,并且能够响应指针和键盘事件。如果你将其切换到div
,它将不再可访问。
实际上,你很少会像我们上面看到的那样修改底层 DOM 元素。更常见的是使用你自己的 React 组件。对于大多数 Trigger
部件来说尤其如此,因为你通常希望将功能与设计系统中的自定义按钮和链接组合在一起。
¥In reality, you will rarely modify the underlying DOM element like we've seen above. Instead it's more common to use your own React components. This is especially true for most Trigger
parts, as you usually want to compose the functionality with the custom buttons and links in your design system.
¥Composing with your own React components
这与上面完全相同,你将 asChild
传递给部件,然后用它封装你自己的组件。然而,也有一些需要注意的陷阱。
¥This works exactly the same as above, you pass asChild
to the part and then wrap your own component with it.
However, there are a few gotchas to be aware of.
¥Your component must spread props
当 Radix 克隆你的组件时,它会传递自身的 props 和事件处理程序,以使其功能齐全且易于访问。如果你的组件不支持这些属性,它将崩溃。
¥When Radix clones your component, it will pass its own props and event handlers to make it functional and accessible. If your component doesn't support those props, it will break.
这是通过将所有 props 展开到底层 DOM 节点上来实现的。
¥This is done by spreading all of the props onto the underlying DOM node.
我们建议你始终这样做,这样你就不必担心实现细节(例如,要接受哪些属性/事件)。我们发现,这通常适用于 "leaf" 组件。
¥We recommend always doing this so that you are not concerned with implementation details (ie. which props/events to accept). We find this is good practice for "leaf" components in general.
与直接更改元素类型类似,你有责任确保自定义组件渲染的元素类型保持可访问性和功能性。
¥Your component must forward ref
此外,Radix 有时需要将 ref
附加到你的组件(例如,测量其大小)。如果你的组件不接受 ref
,则它将崩溃。
¥Additionally, Radix will sometimes need to attach a ref
to your component (for example to measure its size). If your component doesn't accept a ref
, then it will break.
这是使用 React.forwardRef
实现的(阅读更多 react.dev)。
¥This is done using React.forwardRef
(read more on react.dev).
虽然并非所有组件都需要这样做,但我们建议你始终这样做,这样你就不必担心实现细节。对于叶子组件来说,这通常也是一种很好的做法。
¥Whilst this isn't necessary for all parts, we recommend always doing it so that you are not concerned with implementation details. This is also generally good practice anyway for leaf components.
¥Composing multiple primitives
你可以根据需要深度使用 asChild
。这意味着这是一种将多个基础组件的行为组合在一起的好方法。以下是如何将 Tooltip.Trigger
和 Dialog.Trigger
与你自己的按钮组合在一起的示例:
¥asChild
can be used as deeply as you need to. This means it is a great way to compose multiple primitive's behavior together.
Here is an example of how you can compose Tooltip.Trigger
and Dialog.Trigger
together with your own button: