Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ref #14

Open
lz-lee opened this issue Oct 12, 2019 · 0 comments
Open

Ref #14

lz-lee opened this issue Oct 12, 2019 · 0 comments

Comments

@lz-lee
Copy link
Owner

lz-lee commented Oct 12, 2019

ref

  • 创建 fiber 的时候(调和子节点里面)处理 ref
  • commit 开始前,第二次循环执行 commitAllHostEffectsdom的增、删、改)过程中,调用 commitDetachRefref 从之前挂载的地方卸载
  • 第三次循环执行 commitAllLifeCycles 调用 commitAttachRef更新过后的节点挂载到 ref 上,如果 ref 是函数 则执行 ref 函数fiberinstance 作为参数传入
// createRef
function createRef(): RefObject {
  const refObject = {
    current: null,
  };
  if (__DEV__) {
    Object.seal(refObject);
  }
  return refObject;
}

coerceRef

  • 处理字符串 ref,需要创建一个 ref 方法,挂载在 fiberref 属性上,在这个方法里将更新过的 dom 节点或者 class instance 挂载在实例的 refs[stringRef] 上(即 commitAttachRef 过程)
  • 最终使用是通过 this.refs.stringRef 获取 ref,即将被废弃
// 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

  • commitAllHostEffects 先卸载原有的 ref
// dom 节点的插入、删除、更新
function commitAllHostEffects() {
  while (nextEffect !== null) {
    recordEffect();

    const effectTag = nextEffect.effectTag;

    // 判断内部是否为文字节点
    if (effectTag & ContentReset) {
      // 文字节点设置为空字符串
      commitResetTextContent(nextEffect);
    }

    // ref操作
    if (effectTag & Ref) {
      const current = nextEffect.alternate;
      // 非首次渲染
      if (current !== null) {
        commitDetachRef(current);
      }
    }
    // ...
  }
}

// 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
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;
    }
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant