Forwarding Refs

引用传递是把引用从组件传递到它的后代的方法.这种方法在高阶组件中特别有用.

接下来我们举一个利用高阶组件打印组件属性到控制台的例子:

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”高阶组件把所有属性传递给它包装的组件,所以渲染后的结果将是一样的。例如,我们可以用这个高阶组件记录自身获得的属性,并且把属性传递到我们的”fancy button” 组件:

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不是一个属性。就像key,react用不一样的方式处理它们。 如果你在高阶组件上添加ref属性,ref属性只会指向最外层的容器组件,而不是被包装的组件。

这意味着我们想要ref关联到FancyButton组件,但实际上ref被关联到到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.forwardRefAPI传递外层容器的refs到内部FancyButton组件. React.forwardRef 是入参为propsref并且返回类型是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.
  function forwardRef(props, ref) {
    return <LogProps {...props} forwardedRef={ref} />;
  }

  // These next lines are not necessary,
  // But they do give the component a better display name in DevTools,
  // e.g. "ForwardRef(logProps(MyComponent))"
  const name = Component.displayName || Component.name;
  forwardRef.displayName = `logProps(${name})`;

  return React.forwardRef(forwardRef);
}