Skip to content

commitLayoutEffects

Commit 阶段对于副作用的第三次遍历处理过程。

在这个阶段主要就是执行 DOM 插入完成后的收尾工作。

主要做两件事

  1. 对于不同类型的节点,执行不同的 commit 方法
  • 对于函数组件, 执行 useLayoutEffect 中的 create 方法 并将 create 方法的返回值赋值给 effect 的 destroy 属性
  • 对于类组件,
    • 初次渲染: 执行 componentDidMount 生命周期方法
    • 更新类型: 执行 componentDidUpdate 生命周期方法
    • 执行 所有 setState 的 callback 方法, 并清空 update.effects
  • 对于 DOM 节点
    • 执行 特地 DOM 节点的 focus 方法
  • 对于根节点 HostRoot
    • 执行 所有 setState 的 callback 方法, 并清空 update.effects
  • 对于 Profiler 组件
    • 执行 onRender 方法
  1. 更新最终的 ref 的值

源码讲解

js
/**
 * mounted 后的操作
 * 1. 通过 commitLifeCycles
 *   - 函数组件 useLayoutEffect create函数回调
 *   - class组件   初次渲染 - componentDidMount 生命周期
 *                更新渲染 - componentDidUpdate 生命周期
 * 2. ref.current 的更新
 * @param {*} root
 * @param {*} committedLanes
 */
function commitLayoutEffects(root: FiberRoot, committedLanes: Lanes) {
  // TODO: Should probably move the bulk of this function to commitWork.
  while (nextEffect !== null) {
    setCurrentDebugFiberInDEV(nextEffect);
    const flags = nextEffect.flags;
    // 带有更新或者回调标记的副作用执行( commitLifeCycles as commitLayoutEffectOnFiber)
    if (flags & (Update | Callback)) {
      const current = nextEffect.alternate;
      commitLayoutEffectOnFiber(root, current, nextEffect, committedLanes);
    }
    if (enableScopeAPI) {
      if (flags & Ref && nextEffect.tag !== ScopeComponent) {
        commitAttachRef(nextEffect);
      }
    } else {
      if (flags & Ref) {
        commitAttachRef(nextEffect);
      }
    }
    resetCurrentDebugFiberInDEV();
    nextEffect = nextEffect.nextEffect;
  }
}

FunctionComponent

函数类型的组件:执行 useLayoutEffect 中的 create 方法 并将 create 方法的返回值赋值给 effect 的 destroy 属性

js
switch (finishedWork.tag) {
  case FunctionComponent:
  case ForwardRef:
  case SimpleMemoComponent: {
    if (!enableSuspenseLayoutEffectSemantics || !offscreenSubtreeWasHidden) {
      if (
        enableProfilerTimer &&
        enableProfilerCommitHooks &&
        finishedWork.mode & ProfileMode
      ) {
        try {
          startLayoutEffectTimer();
          // 执行 useLayoutEffect 中的 create 方法
          // 并将 create 方法的返回值赋值给 effect 的 destroy 属性
          commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
        } finally {
          recordLayoutEffectDuration(finishedWork);
        }
      } else {
        commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
      }
    }
    break;
  }
}

ClassComponent

  • 初始渲染 - componentDidMount 生命周期
  • 更新渲染 - componentDidUpdate 生命周期
  • commitUpdateQueue(finishedWork, updateQueue, instance); 执行 effect 的 callback
js
case ClassComponent : {
  // Class 组件的实例对象
  const instance = finishedWork.stateNode;
  if (finishedWork.flags & Update) {
    if (!offscreenSubtreeWasHidden) {
      // 初次渲染的组件 执行 componentDidMount 生命周期方法
      if (current === null) {
        if (
          enableProfilerTimer &&
          enableProfilerCommitHooks &&
          finishedWork.mode & ProfileMode
        ) {
          try {
            startLayoutEffectTimer();
            instance.componentDidMount();
          } finally {
            recordLayoutEffectDuration(finishedWork);
          }
        } else {
          instance.componentDidMount();
        }
      } else {
        // 对于更新的组件, 执行 componentDidUpdate 生命周期方法
        const prevProps =
          finishedWork.elementType === finishedWork.type
            ? current.memoizedProps
            : resolveDefaultProps(finishedWork.type, current.memoizedProps);
        const prevState = current.memoizedState;
      }
      if (
        enableProfilerTimer &&
        enableProfilerCommitHooks &&
        finishedWork.mode & ProfileMode
      ) {
        try {
          startLayoutEffectTimer();
          // 执行 componentDidUpdate 生命周期方法
          // 并将 getSnapshotBeforeUpdate 方法的返回值赋值给 instance.__reactInternalSnapshotBeforeUpdate 属性
          instance.componentDidUpdate(
            prevProps,
            prevState,
            instance.__reactInternalSnapshotBeforeUpdate
          );
        } finally {
          recordLayoutEffectDuration(finishedWork);
        }
      } else {
        instance.componentDidUpdate(
          prevProps,
          prevState,
          instance.__reactInternalSnapshotBeforeUpdate
        );
      }
    }
  }

  // 处理所有 update的 callback 和 清空 update.effects
  const updateQueue = finishedWork.updateQueue;
  if (updateQueue !== null) {
    commitUpdateQueue(finishedWork, updateQueue, instance);
  }
}

HostComponent 元素节点类型

  • 初次渲染的时候,如果存在 autoFocus 执行 focus() 聚焦方法
js
switch (finishedWork.tag) {
  case HostComponent: {
    const instance: Instance = finishedWork.stateNode;
    // 新的节点,且存在更新类型的副作用
    if (current === null && finishedWork.flags & Update) {
      const type = finishedWork.type;
      const props = finishedWork.memoizedProps;
      commitMount(instance, type, props, finishedWork);
    }
    return;
  }
}
/**
 * autoFocus 属性 最后触发元素的 focus() 方法
 * @param {*} domElement
 * @param {*} type
 * @param {*} newProps
 * @param {*} internalInstanceHandle
 */
export function commitMount(
  domElement: Instance,
  type: string,
  newProps: Props,
  internalInstanceHandle: Object
): void {
  if (shouldAutoFocusHostComponent(type, newProps)) {
    ((domElement: any):
      | HTMLButtonElement
      | HTMLInputElement
      | HTMLSelectElement
      | HTMLTextAreaElement).focus();
  }
}