lz-lee / React-Source-Code

React源码阅读笔记
11 stars 0 forks source link

Ref #14

Open lz-lee opened 4 years ago

lz-lee commented 4 years ago

ref

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

coerceRef

// ReactChildFiber.js 调和子节点复用或者创建新的fiber 节点时处理 ref
function coerceRef(
  returnFiber: Fiber,
  current: Fiber | null,
  element: ReactElement,
) {
  let mixedRef = element.ref;
  if (
    mixedRef !== null &&
    typeof mixedRef !== 'function' &&
    typeof mixedRef !== 'object'
  ) {
    // 处理字符串 ref 
    // createElement -> 传入 owner 为 ReactCurrentOwner.current ,而在 finishClassComponent 中设置 ReactCurrentOwner.current= workInProgress;
    // 之后子节点会在 instance.render 当中被创建,进入到调和子节点中
    // _owner 即 fiber 对象
    if (element._owner) {
      const owner: ?Fiber = (element._owner: any);
      let inst;
      if (owner) {
        const ownerFiber = ((owner: any): Fiber);
        // classComponent 的实例
        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 // _stringRef 如果没有变化,不需要重新生成 ref  方法
      ) {
        return current.ref;
      }
      const ref = function(value) {
        //  即组件里使用的 this.refs 
        let refs = inst.refs;
        if (refs === emptyRefsObject) {
          // This is a lazy pooled frozen object, so we need to initialize.
          refs = inst.refs = {};
        }
        // dom 节点 或者 instance 被挂载的时候会调用 ref 这个方法,value 即传入它自己的实例
        if (value === null) {
          delete refs[stringRef];
        } else {
          // 设置到 this.refs 的 stringRef 属性上
          refs[stringRef] = value;
        }
      };
      ref._stringRef = stringRef;
      // 这个方法在 commit 阶段被调用
      return ref;
    } else {
      // 。。。
    }
  }
  return mixedRef;
}

commitDetachRef

// commitDetachRef

function commitDetachRef(current: Fiber) { const currentRef = current.ref; if (currentRef !== null) { if (typeof currentRef === 'function') { // 如果是函数则传入 null 执行 currentRef(null); } else { // 其他类型直接赋值 null currentRef.current = null; } } }


### commitAttachRef
- `commitAllLifeCycles` 中将更新后的 `instance` 挂载到 `ref` 上

```js
function commitAttachRef(finishedWork: Fiber) {
  const ref = finishedWork.ref;
  if (ref !== null) {
    const instance = finishedWork.stateNode;
    let instanceToUse;
    // 获取 instance 
    switch (finishedWork.tag) {
      case HostComponent:
        instanceToUse = getPublicInstance(instance);
        break;
      default:
        instanceToUse = instance;
    }
    if (typeof ref === 'function') {
      // 这里即函数式 ref 真正挂载 instance, 字符串 ref 创建的 ref 函数也在这执行,将 this.refs[stringRef] = instanceToUse
      ref(instanceToUse);
    } else {
      // 其他类型直接赋值 ref.current
      ref.current = instanceToUse;
    }
  }
}