内容简介:本文为意译,翻译过程中掺杂本人的理解,如有误导,请放弃继续阅读。原文地址:Forwarding RefsRef forwarding是一种将ref钩子自动传递给组件的子孙组件的技术。对于应用的大部分组件,这种技术并不是那么的必要。然而,它对于个别的组件还是特别地有用的,尤其是那些可复用组件的类库。下面的文档讲述的是这种技术的最常见的应用场景。
本文为意译,翻译过程中掺杂本人的理解,如有误导,请放弃继续阅读。
原文地址:Forwarding Refs
Ref forwarding是一种将ref钩子自动传递给组件的子孙组件的技术。对于应用的大部分组件,这种技术并不是那么的必要。然而,它对于个别的组件还是特别地有用的,尤其是那些可复用组件的类库。下面的文档讲述的是这种技术的最常见的应用场景。
正文
传递refs到DOM components
假设我们有一个叫FancyButton的组件,它负责在界面上渲染出一个原生的DOM元素-button:
function FancyButton(props) { return ( <button className="FancyButton"> {props.children} </button> ); } 复制代码
一般意义来说,React组件就是要隐藏它们的实现细节,包括自己的UI输出。而其他引用了 <FancyButton>
的组件也不太可能想要获取ref,然后去访问 <FancyButton>
内部的原生DOM元素button。在组件间相互引用的过程中,尽量地不要去依赖对方的DOM结构,这属于一种理想的使用场景。
对于一些应用层级下的组件,比如 <FeedStory>
和 <Comment>
组件(原文档中,没有给出这两个组件的实现代码,我们只能顾名思义了),这种封装性是我们乐见其成的。但是,这种封装性对于达成某些“叶子”(级别的)组件(比如, <FancyButton>
和 <MyTextInput>
)的高可复用性是十分的不方便的。因为在项目的大部分场景下,我们往往是打算把这些“叶子”组件都当作真正的DOM节点button和input来使用的。这些场景可能是管理元素的聚焦,文本选择或者动画相关的操作。对于这些场景,访问组件的真正DOM元素是在所难免的了。
Ref forwarding是组件一个可选的特征。一个组件一旦有了这个特征,它就能接受上层组件传递下来的ref,然后顺势将它传递给自己的子组件。
在下面的例子当中, <FancyButton>
通过React.forwardRef的赋能,它可以接收上层组件传递下来的ref,并将它传递给自己的子组件-一个原生的DOM元素button:
const FancyButton = React.forwardRef((props, ref) => ( <button ref={ref} className="FancyButton"> {props.children} </button> )); // 假如你没有通过 React.createRef的赋能,在function component上你是不可以直接挂载ref属性的。 // 而现在你可以这么做了,并能访问到原生的DOM元素: const ref = React.createRef(); <FancyButton ref={ref}>Click me!</FancyButton>; 复制代码
通过这种方式,使用了 <FancyButton>
的组件就能通过挂载ref到 <FancyButton>
组件的身上来访问到对应的底层的原生DOM元素了-就像直接访问这个DOM元素一样。
下面我们逐步逐步地来解释一下上面所说的是如何发生的:
- 我们通过调用React.createRef来生成了一个React ref,并且把它赋值给了ref变量。
-
我们通过手动赋值给
<FancyButton>
的ref属性进一步将这个React ref传递下去。 -
接着,React又将ref传递给React.forwardRef()调用时传递进来的函数
(props, ref) => ...
。届时,ref将作为这个函数的第二个参数。 -
在
(props, ref) => ...
组件的内部,我们又将这个ref 传递给了作为UI输出一部分的<button ref={ref}>
组件。 -
当
<button ref={ref}>
组件被真正地挂载到页面的时候,,我们就可以在使用ref.current
来访问真正的DOM元素button了。
注意,上面提到的第二个参数ref只有在你通过调用React.forwardRef()来定义组件的情况下才会存在。普通的function component和 class component是不会收到这个ref参数的。同时,ref也不是props的一个属性。
Ref forwarding技术不单单用于将ref传递到DOM component。它也适用于将ref传递到class component,以此你可以获取这个class component的实例引用。
组件类库维护者的注意事项
当你在你的组件类库中引入了forwardRef,那么你就应该把这个引入看作一个breaking change,并给你的类库发布个major版本。这么说,是因为一旦你引入了这个特性,那你的类库将会表现得跟以往是不同( 例如:what refs get assigned to, and what types are exported),这将会打破其他依赖于老版ref功能的类库和整个应用的正常功能。
我们得有条件地使用 React.forwardRef
,即使有这样的条件,我们也推荐你能不用就不要用。理由是: React.forwardRef
会改变你类库的行为,并且会在用户升级React版本的时候打破用户应用的正常功能。
高阶组件里的Forwarding refs
这种技术对于高阶组件来说也是特别有用的。假设,我们要实现一个打印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
将所有的props都照样传递给了 WrappedComponent
,所以高阶组件的UI输出和 WrappedComponent
的UI输出将会一样的。举个例子,我们将会使用这个高阶组件来把我们传递给 <FancyButton>
的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实际上并没有被传递下去(到 WrappedComponent
组件中)。这是因为ref并不是真正的prop。正如key一样,它们都不是真正的prop,而是被用于React的内部实现。像上面的例子那样给一个高阶组件直接传递ref,那么这个ref指向的将会是(高阶组件所返回)的containercomponent实例而不是wrapper component实例:
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接收一个render function,这个render function将会得到两个实参:props和ref。举例如下:
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} />; - return <Component {...this.props} />; } } // 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} />; + }); } 复制代码
在DevTools里面显示一个自定义的名字
React.forwardRef接收一个render function。React DevTools将会使用这个function来决定将ref forwarding component名显示成什么样子。
举个例子,下面的WrappedComponent就是ref forwarding component。它在React DevTools将会显示成“ForwardRef”:
const WrappedComponent = React.forwardRef((props, ref) => { return <LogProps {...props} forwardedRef={ref} />; }); 复制代码
假如你给render function命名了,那么React DevTools将会把这个名字包含在ref forwarding component名中(如下,显示为“ForwardRef(myFunction)”):
const WrappedComponent = React.forwardRef( function myFunction(props, ref) { return <LogProps {...props} forwardedRef={ref} />; } ); 复制代码
你甚至可以把wrappedComponent的名字也囊括进来,让它成为render function的displayName的一部分(如下,显示为“ForwardRef(logProps(${ wrappedComponent.name }))”):
function logProps(Component) { class LogProps extends React.Component { // ... } function forwardRef(props, ref) { return <LogProps {...props} forwardedRef={ref} />; } // Give this component a more helpful display name in DevTools. // e.g. "ForwardRef(logProps(MyComponent))" const name = Component.displayName || Component.name; forwardRef.displayName = `logProps(${name})`; return React.forwardRef(forwardRef); } 复制代码
这样一来,你就可以看到一条清晰的refs传递路径:React.forwardRef -> logProps -> wrappedComponent。如果这个wrappeedComponent是我们上面用React.forwardRef包裹的 FancyButton
,这条路径可以更长:React.forwardRef -> logProps -> React.forwardRef -> FancyButton -> button。
以上所述就是小编给大家介绍的《[译]React高级话题之Forwarding Refs》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 聊一聊个人成长这个话题
- [译]React高级话题之高阶组件
- [译]React高级话题之Context
- [译] React 高级话题之 Render Props
- [译]React高级话题之Error Boundaries
- Selenium自动化发布话题(Python实现)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。