一般状况下,咱们想获取一个组建或则一个HTML元素的实例经过 Ref特性 就能够实现,可是某些时候咱们须要在子父级组建中传递使用实例,Forwarding Refs提供了一种技术手段来知足这个要求,特别是开发一些重复使用的组建库时。好比下面的例子:javascript
function MyButton(props) { return ( <button className="MyButton"> {props.children} </button> ); }
上面的代码中MyButton组件渲染了一个HTML元素。对于使用者而言,React隐藏了将代码渲染成页面元素的过程,当其余组件使用MyButton时,并无任何直接的方法来获取MyButton中的<button>元素,这样的设计方法有利于组建的分片管理,下降耦合。java
可是像MyButton这样的组建,其实仅仅是对基本的HTML元素进行了简单的封装。某些时候,上层组建使用他时更但愿将其做为一个基本的HTML元素来看待,实现某些效果须要直接操做DOM,好比focus、selection和animations效果。react
下面的例子将Forwarding Refs添加到MyButton组件中,以实现实例传递的效果。app
const MyButton = React.forwardRef((props, ref) => ( <button ref={ref} className="MyButton"> {props.children} </button> )); // 经过ref能够直接操做<button>元素: const ref = React.createRef(); <MyButton ref={ref}>Click me!</MyButton>;
这个时候,ref能够直接操做<button>元素。其实执行过程很是简单,也就下面5步:dom
须要注意的是只有使用React.forwardRef来建立一个组件时,第二个ref参数才会存在。固定的方法或者使用类来建立组件并不会接收到ref参数。Forwarding Refs特性并不单单局限于用在HTML DOM元素上,这种方式也实用于组件之间传递Ref。 工具
高阶组件(HOCs)仅仅对通常组件的包装。通常组件被包装以后对于使用者来讲并不清晰其是不是被包装过,此时使用Ref获得的是高阶组件的实例。所以Forwarding Refs特性对于高阶组件来讲更有价值。ui
下面是一个高阶组件记录日志的例子:this
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中的数据。咱们用其包装前面的MyButton组件。spa
class MyButton extends React.Component { focus() { // ... } render() { // } } export default logProps(MyButton);
此时经过import并使用Refs实际上获得的是LogProps的实例:设计
import FancyButton from './FancyButton'; const ref = React.createRef(); <MyButton label="Click Me" handleClick={handleClick} ref={ref} />;
咱们使用Forwarding Refs对高阶组件进行简单的改造便可解决这个问题:
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; // 经过forwardedRef参数传递ref的值 return <Component ref={forwardedRef} {...rest} />; } } //而后使用 React.forwardRef 来包装建立 LogProps组件的实例 //注意这里使用 forwardedRef 来传递 父组件的 ref // return React.forwardRef((props, ref) => { return <LogProps {...props} forwardedRef={ref} />; }); }
若是咱们不进行任何调整,下面的代码在调试工具中输出的组件名称为:"ForwardRef(MyComonent)":
const WrappedComponent = React.forwardRef( function myFunction(props, ref) { return <LogProps {...props} forwardedRef={ref} />; } );
能够经过displayName来设定想要现实的名字:
function logProps(Component) { class LogProps extends React.Component { // ... } //先定义返回的高阶组件方法 function forwardRef(props, ref) { return <LogProps {...props} forwardedRef={ref} />; } //而后设定这个组件的名称 const name = Component.displayName || Component.name; forwardRef.displayName = `logProps(${name})`; //构建组件 return React.forwardRef(forwardRef); }