Appearance
commitLayoutEffects
Commit阶段对于副作用的第三次遍历处理过程。
在这个阶段主要就是执行DOM插入完成后的收尾工作。
其主要作用如下:
ref.current 的更新
Class组件类型:
- 初始渲染 - componentDidMount 生命周期
- 更新渲染 - componentDidUpdate 生命周期
函数式组件类型:
- 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();
}
}