React.createRef()/forwardRef() for React source parsing

Posted by iyia12co on Tue, 15 Oct 2019 21:59:11 +0200

I. React.createRef()
GitHub:
https://github.com/AttackXiaoJinJin/reactExplain/blob/master/react16.8.6/packages/react/src/ReactCreateRef.js

Effect:
Get the DOM instance of the target element

Use:

import React from 'react'

export default class Father extends  React.Completed{
  constructor(props){
    super(props)
    this.father=React.createRef()
  }

  componentDidMount(){
    this.father.current.value='hahhaha'
  }

  render(){
    return <div ref={this.father}>
      this is div
    </div>
  }
  
}

Source code:

import type {RefObject} from 'shared/ReactTypes';

// an immutable object with a single mutable value
//An immutable object that modifies value
//I haven't seen this style of writing: RefObject
export function createRef(): RefObject {
  //Initialize ref object, property current initial value is null
  const refObject = {
    current: null,
  };
  if (__DEV__) {
    Object.seal(refObject);
  }
  return refObject;
}

Analysis:
The source code is relatively simple, that is, the refObject with current attribute is returned.

II. React.forwardRef()
GitHub:
https://github.com/AttackXiaoJinJin/reactExplain/blob/master/react16.8.6/packages/react/src/forwardRef.js

Effect:
Getting a child component from the parent component is a DOM instance of the FunctionComponent

Use:

import React from 'react'
//There is no dom instance for function component, because it is PureComponent, so there is no this.
// So we can't get the instance through createRef().

//Pass the parent of the parent to the child component, and bind the DOM instance of the child component, so that the parent component can get the DOM instance of the child component.
const Child=React.forwardRef((props,ref)=>{
  return <div ref={ref}>child div</div>
})

export default class Father extends  React.Completed{
  constructor(props){
    super(props)
    this.father=React.createRef()
  }

  componentDidMount(){
    this.father.current.value='hahhaha'
  }

  render(){
    return <Child ref={this.father} />
  }

}

Source code:

import {REACT_FORWARD_REF_TYPE, REACT_MEMO_TYPE} from 'shared/ReactSymbols';

import warningWithoutStack from 'shared/warningWithoutStack';

export default function forwardRef<Props, ElementType: React$ElementType>(
  render: (props: Props, ref: React$Ref<ElementType>) => React$Node,
) {
  //__Don't look at DEV
  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?',
      );
    }
  }

  return {
    //When wrapped by forwardRef, $$typeof inside the component is react? Forward? Ref? Type
    $$typeof: REACT_FORWARD_REF_TYPE,
    //render is the wrapped function component, and ClassComponent does not use forwardRef.
    render,
  };
}

Analysis:
(1) if you don't see DEV, the returned Object is also an Object. That is to say, after Child is wrapped by forwardRef, the $$type of React.forwardRef(Child) is react.forward.ref.type.

Be careful:
Once the Child component is referenced by JSX in the other component, it is React.createElement(React.forwardRef(Child)), which wraps another layer. At this time, $$typeof 'is' react \ element \ type', ` type 'is' React.forwardRef(Child),' $$typeof 'in' type 'is react \ forward \ type

const ReactElement = function(type,...) {
  const element = {
    $$typeof: REACT_ELEMENT_TYPE,
    type: type,
  };
}

(2) for the use of forward in high-level components, please refer to: https://reactjs.org/docs/react-api.html#reactforwardref

(3) how to use it in antdPro/FunctionComponent:
Son:

const Child = (props,ref) => {
  const inputRef = React.useRef();
  React.useImperativeHandle(ref, () => ({
    focus: () => {
      // inputRef.current.focus();
      inputRef.current.value='aaaa'
    }
  }));

  return (<input type="text" ref={inputRef}/>)
}

export default React.forwardRef(Child)

Father:

import Child from './Child';
const Father=(props)=> {
   const rref= React.useRef(null)
   useEffect(() => {
    //console.log(rref.current,'rref33')
    rref.current.focus()
  }, []);
return (<Child ref={rref}/>)
}

Be careful:
① if it is used in antdPro, it is not easy for me to wrap it with the connect ion of the dva. The author on the issue did not answer, so it was closed: https://github.com/ant-design/ant-design-pro/issues/3123

(2) useImperativeMethods has been renamed as useImperativeHandle, and the portal: https://github.com/facebook/react/pull/14565

(end)

Topics: Javascript React github Attribute