coerceRef

在调和子节点得过程中,会对ReactElement中得ref进行处理,主要是处理string ref,把他转换成一个方法,这个方法主要做的事情就是设置instance.refs[stringRef] = element,相当于把他转换成了function ref

对于更新得过程中string ref是否变化需要对比得是current.ref._stringRef,这里记录了上一次渲染得时候如果使用得是string ref他的值是什么

owner是在调用createElement的时候获取的,通过ReactCurrentOwner.current获取,这个值在更新一个组件前会被设置,比如更新ClassComponent的时候,调用render方法之前会设置,然后调用render的时候就可以获取对应的owner了。

// 调用
existing.ref = coerceRef(returnFiber, current, element)

function coerceRef(
  returnFiber: Fiber,
  current: Fiber | null,
  element: ReactElement,
) {
  let mixedRef = element.ref
  if (
    mixedRef !== null &&
    typeof mixedRef !== 'function' &&
    typeof mixedRef !== 'object'
  ) {
    // dev code

    if (element._owner) {
      const owner: ?Fiber = (element._owner: any)
      let inst
      if (owner) {
        const ownerFiber = ((owner: any): Fiber)
        // 提醒
        inst = ownerFiber.stateNode
      }
      // 提醒
      const stringRef = '' + mixedRef
      // Check if previous string ref matches new string ref
      if (
        current !== null &&
        current.ref !== null &&
        typeof current.ref === 'function' &&
        current.ref._stringRef === stringRef
      ) {
        return current.ref
      }
      const ref = function(value) {
        let refs = inst.refs
        if (refs === emptyRefsObject) {
          // This is a lazy pooled frozen object, so we need to initialize.
          refs = inst.refs = {}
        }
        if (value === null) {
          delete refs[stringRef]
        } else {
          refs[stringRef] = value
        }
      }
      ref._stringRef = stringRef
      return ref
    } else {
      // 提醒
    }
  }
  return mixedRef
}

commitAttachRef

commitRoot最后阶段,commitAllLifecycle的时候,如果又Refside effect,则会调用该方法

该方法很简单,获取ref作用得节点,并根据ref的类型,调用方法,或者赋值ref.current,因为ref已经在coerceRef处理过了,不会存在还是string得情况。

function commitAttachRef(finishedWork: Fiber) {
  const ref = finishedWork.ref
  if (ref !== null) {
    const instance = finishedWork.stateNode
    let instanceToUse
    switch (finishedWork.tag) {
      case HostComponent:
        instanceToUse = getPublicInstance(instance)
        break
      default:
        instanceToUse = instance
    }
    if (typeof ref === 'function') {
      ref(instanceToUse)
    } else {
      ref.current = instanceToUse
    }
  }
}

commitDetachRef

卸载ref,设置为null

commitAllHostEffects的时候会统一对所有有refside effect的节点先卸载ref,后续会统一再设置。

function commitDetachRef(current: Fiber) {
  const currentRef = current.ref
  if (currentRef !== null) {
    if (typeof currentRef === 'function') {
      currentRef(null)
    } else {
      currentRef.current = null
    }
  }
}

results matching ""

    No results matching ""