# Ref

# createRef

createRef 源码只有几句如下:

export function createRef(): RefObject {
  const refObject = {
    current: null,
  };
  if (__DEV__) {
    Object.seal(refObject);
  }
  return refObject;
}

作者源码注释写的是这个函数返回一个不变的对象,这个对象有一个可变的 current 属性。

React.createRef 是用在类组件里面的,不能在函数组件上使用 ref 属性,因为它们没有实例

如果要在函数组件上使用ref ,可以使用 forwardRef

# forwardRef

export default function forwardRef<Props, ElementType: React$ElementType>(
  render: (props: Props, ref: React$Ref<ElementType>) => React$Node,
) {
  return {
    $$typeof: REACT_FORWARD_REF_TYPE,
    render,
  };
}

当 tag 为 ForwardRef,便会走到 updateForwardRef 分支 会走 renderWithHooks,自己看吧,使用方法如下:

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} />
  }
}

# useRef

useRef 分创建阶段和修改呢阶段

function mountRef<T>(initialValue: T): {|current: T|} {
  const hook = mountWorkInProgressHook();
  const ref = {current: initialValue};
  if (__DEV__) {
    Object.seal(ref);
  }
  hook.memoizedState = ref;
  return ref;
}

function updateRef<T>(initialValue: T): {|current: T|} {
  const hook = updateWorkInProgressHook();
  return hook.memoizedState;
}

源码非常简单 就是把 ref 挂载到 memoizedState 然后使用时再去 memoizedState 取值

# createRef 和 useRef 区别

useRef 仅能用在 FunctionComponent,createRef 仅能用在 ClassComponent。createRef 并没有 Hooks 的效果,其值会随着 FunctionComponent 重复执行而不断被初始化:

function App() {
  // 错误用法,永远也拿不到 ref
  const valueRef = React.createRef();
  return <div ref={valueRef} />;
}

上述 valueRef 会随着 App 函数的 Render 而重复初始化,这也是 Hooks 的独特之处,虽然用在普通函数中,但在 React 引擎中会得到超出普通函数的表现,比如初始化仅执行一次,或者引用不变。

但是 createRef 为什么在 ClassComponent 就可以呢,因为在类组件中只会初始化一次,有自己完整的生命周期。

更新时间: 12/9/2020, 9:35:16 PM