前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >React源码解析之React.createRef()/forwardRef()

React源码解析之React.createRef()/forwardRef()

作者头像
进击的小进进
发布2019-09-05 17:12:08
1.5K0
发布2019-09-05 17:12:08
举报

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

作用: 获取目标elementDOM实例

使用:

代码语言:javascript
复制
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>
  }

}

源码:

代码语言:javascript
复制
import type {RefObject} from 'shared/ReactTypes';

// an immutable object with a single mutable value
//可修改value的 不可变的对象
//没见过这种写法 :RefObject
export function createRef(): RefObject {
  //初始化ref对象,属性current初始值为null
  const refObject = {
    current: null,
  };
  if (__DEV__) {
    Object.seal(refObject);
  }
  return refObject;
}

解析: 源码比较简单,就是返回了带有current属性的refObject


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

作用: 从父组件中获取子组件是FunctionComponentDOM实例

使用:

代码语言:javascript
复制
import React from 'react'
//funciton component是没有dom实例的,因为它是PureComponent,所以没有this,
// 所以不能通过createRef()来拿到实例

//将Father的father传给子组件,并绑定子组件的DOM实例,从而能在父组件拿到子组件的DOM实例
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} />
  }

}

源码:

代码语言:javascript
复制
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,
) {
  //__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 {
    //被forwardRef包裹后,组件内部的$$typeof是REACT_FORWARD_REF_TYPE
    $$typeof: REACT_FORWARD_REF_TYPE,
    //render即包装的FunctionComponent,ClassComponent是不用forwardRef的
    render,
  };
}

解析: (1)不看__DEV__的话,返回的也是一个Object,也就是说,ChildforwardRef包裹后,React.forwardRef(Child)$$typeofREACT_FORWARD_REF_TYPE

注意: 一旦在Father组件中,用JSX引用了Child组件,那么就是React.createElement(React.forwardRef(Child)),又包裹了一层,此时的$$typeofREACT_ELEMENT_TYPEtypeReact.forwardRef(Child)type里面的$$typeofREACT_FORWARD_REF_TYPE

代码语言:javascript
复制
const ReactElement = function(type,...) {
  const element = {
    $$typeof: REACT_ELEMENT_TYPE,
    type: type,
  };
}

(2)关于forward在高阶组件的用法,请参考:https://reactjs.org/docs/react-api.html#reactforwardref

(3)如何在antdPro/FunctionComponent中使用: 子:

代码语言:javascript
复制
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)

父:

代码语言:javascript
复制
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}/>)
}

注意:

antdPro中使用的话,我试了是不好用dvaconnect包裹的,issue上作者也没回答,就关闭了:https://github.com/ant-design/ant-design-pro/issues/3123

useImperativeMethods已经重命名为useImperativeHandle,传送门:https://github.com/facebook/react/pull/14565


本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-07-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 webchen 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档