React17 高级指引 Refs转发
Ref 转发是一项将 ref 自动地通过组件传递到其一子组件的技巧。
对于大多数应用中的组件来说,这通常不是必需的。但其对某些组件,尤其是可重用的组件库是很有用的。
转发 refs 到 DOM 组件
考虑这个渲染原生 DOM 元素 button
的 FancyButton
组件:
1 | function FancyButton(props) { |
React 组件隐藏其实现细节,包括其渲染结果。其他使用 FancyButton
的组件通常不需要获取内部的 DOM 元素 button
的 ref。这很好,因为这防止组件过度依赖其他组件的 DOM 结构。
TIP
Ref 转发是一个可选特性,其允许某些组件接收
ref
,并将其向下传递(换句话说,“转发”它)给子组件。
在下面的示例中,FancyButton
使用 React.forwardRef
来获取传递给它的 ref
,然后转发到它渲染的 DOM button
:
1 | const FancyButton = React.forwardRef((props, ref) => ( |
这样,使用 FancyButton
的组件可以获取底层 DOM 节点 button
的 ref ,并在必要时访问,就像其直接使用 DOM button
一样。
- 我们通过调用
React.createRef
创建了一个 React ref 并将其赋值给ref
变量。 - 我们通过指定
ref
为 JSX 属性,将其向下传递给<FancyButton ref={ref}>
。 - React 传递
ref
给forwardRef
内函数(props, ref) => ...
,作为其第二个参数。 - 我们向下转发该
ref
参数到<button ref={ref}>
,将其指定为 JSX 属性。 - 当 ref 挂载完成,
ref.current
将指向<button>
DOM 节点。
TIP
第二个参数
ref
只在使用React.forwardRef
定义组件时存在。常规函数和 class 组件不接收ref
参数,且 props 中也不存在ref
。Ref 转发不仅限于 DOM 组件,你也可以转发 refs 到 class 组件实例中。
组件库维护者的注意事项
TIP
当你开始在组件库中使用
forwardRef
时,你应当将其视为一个破坏性更改,并发布库的一个新的主版本。
这是因为你的库可能会有明显不同的行为(例如 refs 被分配给了谁,以及导出了什么类型),并且这样可能会导致依赖旧行为的应用和其他库崩溃。
出于同样的原因,当 React.forwardRef
存在时有条件地使用它也是不推荐的:它改变了你的库的行为,并在升级 React 自身时破坏用户的应用。
在高阶组件中转发 refs
这个技巧对高阶组件(也被称为 HOC)特别有用。
让我们从一个输出组件 props 到控制台的 HOC 示例开始:
1 | function logProps(WrappedComponent) { class LogProps extends React.Component { |
logProps
HOC 透传(pass through)所有 props
到其包裹的组件,所以渲染结果将是相同的。
1 | class FancyButton extends React.Component { |
上面的示例有一点需要注意:refs 将不会透传下去。这是因为 ref
不是 prop 属性。就像 key
一样,其被 React 进行了特殊处理。
如果你对 HOC 添加 ref,该 ref 将引用最外层的容器组件,而不是被包裹的组件。
在 DevTools 中显示自定义名称
React.forwardRef
接受一个渲染函数。React DevTools 使用该函数来决定为 ref 转发组件显示的内容。
例如,以下组件将在 DevTools 中显示为 “ForwardRef”:
1 | const WrappedComponent = React.forwardRef((props, ref) => { |
如果你命名了渲染函数,DevTools 也将包含其名称(例如 “*ForwardRef(myFunction)*”):
1 | const WrappedComponent = React.forwardRef( |
你甚至可以设置函数的 displayName
属性来包含被包裹组件的名称:
1 | function logProps(Component) { |