Skip to content

commitLayoutEffects

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

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

其主要作用如下:

  1. ref.current 的更新

  2. Class组件类型:

    • 初始渲染 - componentDidMount 生命周期
    • 更新渲染 - componentDidUpdate 生命周期
  3. 函数式组件类型:

    • useLayoutEffect create函数回调

源码讲解

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

commitLifeCycles()

commitUnmount 方法差不多也是根据不同类型的FiberNode执行其生命周期函数

ClassComponent

  • 初始渲染 - componentDidMount 生命周期
  • 更新渲染 - componentDidUpdate 生命周期
  • commitUpdateQueue(finishedWork, updateQueue, instance); 执行 effect 的 callback
js
    case ClassComponent: {
      const instance = finishedWork.stateNode;
      if (finishedWork.flags & Update) {
        if (current === null) {
            // current === null 初次渲染 执行 componentDidMount
            instance.componentDidMount();
        } else {
          const prevProps =
            finishedWork.elementType === finishedWork.type
              ? current.memoizedProps
              : resolveDefaultProps(finishedWork.type, current.memoizedProps);
          const prevState = current.memoizedState;
            // current != null 更新渲染 执行 componentDidUpdate
            instance.componentDidUpdate(
              prevProps,
              prevState,
              instance.__reactInternalSnapshotBeforeUpdate,
            );
      
        }
      }

      // TODO: I think this is now always non-null by the time it reaches the
      // commit phase. Consider removing the type check.
      const updateQueue: UpdateQueue<
        *,
      > | null = (finishedWork.updateQueue: any);
        // We could update instance props and state here,
        // but instead we rely on them being set during last render.
        // TODO: revisit this when we implement resuming.
        commitUpdateQueue(finishedWork, updateQueue, instance);
      }
      return;
    }

FunctionalComponent

  • 执行的是 useLayoutEffect 的 create, 并将返回值作为 destroy 付给 effect.destroy
  • schedulePassiveEffects(finishedWork)
js
    case Block: {
      // 执行的是 useLayoutEffect 的 create
      commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
      // 有一次 执行useLayout的副作用的effect,并将其加入到 schedule任务
      schedulePassiveEffects(finishedWork);
      return;
    }

HostComponent 原生组件类型

  • 初次渲染的时候,如果存在 autoFocus 执行 focus() 聚焦方法
js
  case HostComponent: {
      const instance: Instance = finishedWork.stateNode;

      // Renderers may schedule work to be done after host components are mounted
      // (eg DOM renderer may schedule auto-focus for inputs and form controls).
      // These effects should only be committed when components are first mounted,
      // aka when there is no current/alternate.
      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 {
  // Despite the naming that might imply otherwise, this method only
  // fires if there is an `Update` effect scheduled during mounting.
  // This happens if `finalizeInitialChildren` returns `true` (which it
  // does to implement the `autoFocus` attribute on the client). But
  // there are also other cases when this might happen (such as patching
  // up text content during hydration mismatch). So we'll check this again.
  if (shouldAutoFocusHostComponent(type, newProps)) {
    ((domElement: any):
      | HTMLButtonElement
      | HTMLInputElement
      | HTMLSelectElement
      | HTMLTextAreaElement).focus();
  }
}