注
Ref 转发是一种自动将 ref
通过组件传递给子组件的技术。对于应用程序中的大多数组件,这通常不是必需的。但是,它对某些组件很有用,特别是在可重用的组件库中。最常见的情况如下所述。
refs
给 DOM 组件考虑一个渲染原生 button
DOM 元素的 FancyButton
组件:
function FancyButton(props) { return ( <button className="FancyButton"> {props.children} </button> ); }
React 组件隐藏他们的实现细节,包括它们的渲染输出。使用 FancyButton
的其他组件 通常不需要 获得 ref
到内部 button
DOM 元素。这很好,因为它可以防止组件过多依赖彼此的 DOM 结构。
虽然这样的封装对于像 FeedStory
或 Comment
这样的应用程序级组件是可取的, 对于像 FancyButton
或 MyTextInput
这样的可高度重用的 “leaf” 组件来说可能会很不方便。 这些组件倾向于以与常规 DOM button 和 input 类似的方式在整个应用程序中使用, 并且访问他们的 DOM 节点可能不可避免地用于管理 焦点(focus
),选择(selection
)或动画(animations
)。
Ref 转发是一种选择性加入的功能,可让某些组件接收他们收到的 ref,并将其向下传递(换句话说,“转发”)给孩子。
在下面的例子中, FancyButton
使用 React.forwardRef
来获取传递给它的 ref
, 然后将其转发给它渲染的的 DOM button
:
const FancyButton = React.forwardRef((props, ref) => ( <button ref={ref} className="FancyButton"> {props.children} </button> )); // You can now get a ref directly to the DOM button: const ref = React.createRef(); <FancyButton ref={ref}>Click me!</FancyButton>;
通过这种方式,使用 FancyButton
的组件可以获得底层 button
DOM 节点的引用并在必要时访问它 - 就像他们直接使用 DOM button
一样。
以下是对上述示例中发生情况逐步的说明:
React.createRef
创建一个 React ref
并将其分配给 ref 变量。JSX
属性的 <FancyButton ref={ref}>
。forwardRef
中的 (props, ref) => ...
函数作为第二个参数。JSX
属性的 <button ref = {ref}>
。ref.current
将指向 <button>
DOM节点。注意
第二个 ref
参数仅在使用 React.forwardRef
调用定义组件时才存在。常规函数或类组件不接收 ref
参数,而且 props
也不提供 ref
。
Ref
转发不限于 DOM 组件。您也可以将 refs
转发给类组件实例。
该技术对于 高阶组件(也称为HOC)也特别有用。 让我们从一个 HOC示例开始,将组件 props 记录到控制台:
function logProps(WrappedComponent) { class LogProps extends React.Component { componentDidUpdate(prevProps) { console.log('old props:', prevProps); console.log('new props:', this.props); } render() { return <WrappedComponent {...this.props} />; } } return LogProps; }
“logProps” HOC 将所有 props
传递给它包裹的组件, 所以渲染输出将是相同的。 例如,我们可以使用此 HOC 记录所有传递给 “fancy button
” 组件的 props(属性):
class FancyButton extends React.Component { focus() { // ... } // ... } // Rather than exporting FancyButton, we export LogProps. // It will render a FancyButton though. export default logProps(FancyButton);
上面的例子有一个警告: refs
不会通过。那是因为 ref
不是 props(属性)。 像 key 一样,它的处理方式与 React 不同。 如果你添加一个 ref
到 HOC , 这个 ref
将引用最外面的容器组件,而不是包裹的组件。
这意味着打算给 FancyButton组件的 refs
实际上将被附加到 LogProps
组件:
import FancyButton from './FancyButton'; const ref = React.createRef(); // The FancyButton component we imported is the LogProps HOC. // Even though the rendered output will be the same, // Our ref will point to LogProps instead of the inner FancyButton component! // This means we can't call e.g. ref.current.focus() <FancyButton label="Click Me" handleClick={handleClick} ref={ref} />;
幸运的是,我们可以使用 React.forwardRef
API 明确地将 ref 转发到内部的 FancyButton
组件。 React.forwardRef
接受一个渲染函数,该函数接收 props 和 ref 参数并返回一个 React
节点。例如:
function logProps(Component) { class LogProps extends React.Component { componentDidUpdate(prevProps) { console.log('old props:', prevProps); console.log('new props:', this.props); } render() { const {forwardedRef, ...rest} = this.props; // Assign the custom prop "forwardedRef" as a ref return <Component ref={forwardedRef} {...rest} />; } } // Note the second param "ref" provided by React.forwardRef. // We can pass it along to LogProps as a regular prop, e.g. "forwardedRef" // And it can then be attached to the Component. return React.forwardRef((props, ref) => { return <LogProps {...props} forwardedRef={ref} />; }); }
本文作者:毛超颖
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!