【React源碼解讀】react-ref && forward-ref

react-ref

源碼地址:https://github.com/facebook/react/blob/master/packages/react/src/ReactCreateRef.jsreact

三種使用ref方式git

  • string ref (不被推薦的方式,廢棄)

react會在完成這個節點渲染以後,會在this.refs這個對象上掛載這個string對應的一個key,這個key所指向的就是這個節點的實例對象,若是是dom節點就會對應dom的實例,若是是子組件,就會是子組件的實例(也就是class component) 若是是一個function component,正常來說是會失敗的,也就是undefinedgithub

  • function
  • createRef

使用createRef建立一個對象,這個對象初始的時候,裏面的current是null,把這個對象傳到某個節點,在這個組件渲染以後,會把這個節點掛載到這個對象的currentredux

// demo
import React from 'react'

export default class RefDemo extends React.Component {
  constructor() {
    super()
    this.objRef = React.createRef()

    // { current: null }
  }

  componentDidMount() {
    // console.log(`span1: ${this.refs.ref1.textContent}`)
    // console.log(`span2: ${this.ref2.textContent}`)
    // console.log(`span3: ${this.ref3.current.textContent}`)
    setTimeout(() => {
      this.refs.stringRef.textContent = 'string ref got' 
      this.methodRef.textContent = 'method ref got'
      this.objRef.current.textContent = 'obj ref got'
    }, 1000)
  }

  render() {
    return (
      <>
        <p ref="stringRef">span1</p>
        <p ref={ele => (this.methodRef = ele)}>span3</p>
        <p ref={this.objRef}>span3</p>
      </>
    )
  }
}

// export default () => {
//   return <div>Ref</div>
// }

demo能看到三種方式都成功獲取到了節點,並更新了節點的值dom

// 源碼解讀
import type {RefObject} from 'shared/ReactTypes';

// an immutable object with a single mutable value
export function createRef(): RefObject {
  const refObject = {
    current: null,
  };
  if (__DEV__) {
    Object.seal(refObject);
  }
  return refObject;
}

源碼其實就這短短几行,返回了一個對象,這個對象中有個current屬性,初始爲null學習

存儲一個疑問:這個對象後期如何使用,如何掛載節點?

forward-ref

源碼學習地址:https://github.com/facebook/react/blob/master/packages/react/src/forwardRef.jsthis

考慮以下場景spa

咱們是一個組件開發者,寫了不少供用戶使用的開源組件。用戶使用了例如redux的connect鏈接組件,connect實際上是hoc的模式包裹了一層組件,那麼若是用戶在connect的組件上使用ref,就沒法直接掛載到真實組件上。code

所以

能夠考慮使用React.forwardRef(props, ref) 去傳遞一層refcomponent

// demo
import React from 'react'

const TargetComponent = React.forwardRef((props, ref) => (
  <input type="text" ref={ref} />
))

export default class Comp extends React.Component {
  constructor() {
    super()
    this.ref = React.createRef()
  }

  componentDidMount() {
    this.ref.current.value = 'ref get input'
  }

  render() {
    return <TargetComponent ref={this.ref} />
  }
}
// 源碼解讀
import {REACT_FORWARD_REF_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,
) {
  
  ... 
  
  return {
    $$typeof: REACT_FORWARD_REF_TYPE,
    render,
  };
}

/**
返回的是一個對象,對象中有個$$typeof,切忌不能與createElement中的$$typeof弄混。

拿上述demo來講,因爲經過React.forwardRef返回的是一個對象,所以TargetComponent也是一個對象,而在<TargetComponent />從jsx解析爲js中,解析爲React.createElement(type, config, children)中,TargetComponent只是做爲type。

所以

使用React.forwardRef返回的$$typeof仍然是REACT_ELEMENT_TYPE,它的type是咱們拿到的對象{
    $$typeof: REACT_FORWARD_REF_TYPE,
    render,
  };
它裏面有個$$typeof,是REACT_FORWARD_REF_TYPE

小結:咱們使用React.forwardRef建立的全部的節點,它的$$typeof都是REACT_ELEMENT_TYPE

*/
相關文章
相關標籤/搜索