Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React 源码漂流(五)之 forwardRef #44

Open
sisterAn opened this issue Jul 22, 2019 · 1 comment
Open

React 源码漂流(五)之 forwardRef #44

sisterAn opened this issue Jul 22, 2019 · 1 comment

Comments

@sisterAn
Copy link
Owner

一、forwardRef 用法

React.createRef 中已经介绍过,有三种方式可以使用 React 元素的 ref

ref 是为了获取某个节点的实例,但是 函数式组件(PureComponent) 是没有实例的,不存在 this的,这种时候是拿不到函数式组件的 ref 的。

为了解决这个问题,由此引入 React.forwardRefReact.forwardRef 允许某些组件接收 ref,并将其向下传递给 子组件

const ForwardInput = React.forwardRef((props, ref) => (
  <input ref={ref} />
));

class TestComponent extends React.Component {
  constructor(props) {
    super(props);
    this.inputRef = React.createRef(); // 创建 ref 存储 textRef DOM 元素
  }
  componentDidMount() {
    this.inputRef.current.value = 'forwardRef'    
  }
  render() {
    return ( // 可以直接获取到 ForwardInput input 的 ref:
      <ForwardInput ref={this.inputRef}>
    )
  }
}
  • 只在使用 React.forwardRef 定义组件时,第二个参数 ref 才存在

  • 在项目中组件库中尽量不要使用 React.forwardRef ,因为它可能会导致子组件被 破坏性更改

  • 函数组件 和 class 组件均不接收 ref 参数 ,即 props 中不存在 refref 必须独立 props 出来,否则会被 React 特殊处理掉。

  • 通常在 高阶组件 中使用 React.forwardRef

    function enhance(WrappedComponent) {
      class Enhance extends React.Component {
        componentWillReceiveProps(nextProps) {
          console.log('Current props: ', this.props);
          console.log('Next props: ', nextProps);
        }
        render() {
          const {forwardedRef, ...others} = this.props;
          // 将自定义的 prop 属性 “forwardedRef” 定义为 ref
          return <WrappedComponent ref={forwardedRef} {...others} />;
        }
      }
      // 注意 React.forwardRef 回调的第二个参数 “ref”。
      // 我们可以将其作为常规 prop 属性传递给 Enhance,例如 “forwardedRef”
      // 然后它就可以被挂载到被 Enhance 包裹的子组件上。
      return React.forwardRef((props, ref) => {
        return <Enhance {...props} forwardedRef={ref} />;
      });
    }
    
    // 子组件
    class MyComponent extends React.Component {
      focus() {
        // ...
      }
      // ...
    }
    
    // EnhancedComponent 会渲染一个高阶组件 enhance(MyComponent)
    const EnhancedComponent = enhance(MyComponent);
    
    const ref = React.createRef();
    
    // 我们导入的 EnhancedComponent 组件是高阶组件(HOC)Enhance。
    // 通过React.forwardRef 将 ref 将指向了 Enhance 内部的 MyComponent 组件
    // 这意味着我们可以直接调用 ref.current.focus() 方法
    <EnhancedComponent
      label="Click Me"
      handleClick={handleClick}
      ref={ref}
    />;

二、源码解读

export default function forwardRef<Props, ElementType: React$ElementType>(
  render: (props: Props, ref: React$Ref<ElementType>) => React$Node,
) {
  if (__DEV__) {
    if (render != null && render.$$typeof === REACT_MEMO_TYPE) {
      warningWithoutStack(
        false,
        'forwardRef requires a render function but received a `memo` ' +
          'component. Instead of forwardRef(memo(...)), use ' +
          'memo(forwardRef(...)).',
      );
    } else if (typeof render !== 'function') {
      warningWithoutStack(
        false,
        'forwardRef requires a render function but was given %s.',
        render === null ? 'null' : typeof render,
      );
    } else {
      warningWithoutStack(
        // Do not warn for 0 arguments because it could be due to usage of the 'arguments' object
        render.length === 0 || render.length === 2,
        'forwardRef render functions accept exactly two parameters: props and ref. %s',
        render.length === 1
          ? 'Did you forget to use the ref parameter?'
          : 'Any additional parameter will be undefined.',
      );
    }

    if (render != null) {
      warningWithoutStack(
        render.defaultProps == null && render.propTypes == null,
        'forwardRef render functions do not support propTypes or defaultProps. ' +
          'Did you accidentally pass a React component?',
      );
    }
  }

  /**
   * REACT_FORWARD_REF_TYPE 并不是 React.forwardRef 创建的实例的 $$typeof
   * React.forwardRef 返回的是一个对象,而 ref 是通过实例的参数形式传递进去的,
   * 实际上,React.forwardRef 返回的是一个 ReactElement,它的 $$typeof 也就是 REACT_ELEMENT_TYPE
   * 而 返回的对象 是作为 ReactElement 的 type 存在
   */
  return { // 返回一个对象
    $$typeof: REACT_FORWARD_REF_TYPE, // 并不是 React.forwardRef 创建的实例的 $$typeof
    render, // 函数组件
  };
}
@sisterAn sisterAn changed the title React 源码解读系列(六)之 forwardRef React 源码漂流(六)之 forwardRef Jul 24, 2019
@sisterAn sisterAn changed the title React 源码漂流(六)之 forwardRef React 源码漂流(五)之 forwardRef Jul 24, 2019
@lish1986
Copy link

lish1986 commented Apr 2, 2020

PureComponent不是类组件吗?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants