Appearance
该阶段执行
commitBeforeMutationEffects
函数
完成三件事
- 针对 DOM 节点 的focus 和 blur 状态进行操作(不关注)
- 执行
commitBeforeMutationEffectOnFiber
- 调用
useEffect
回调
commitBeforeMutationEffects
js
function commitBeforeMutationEffects() {
while (nextEffect !== null) {
const current = nextEffect.alternate;
if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {
/// 处理 focus/blur 状态
}
// 根据之前生成的 effectList 进行处理
const flags = nextEffect.flags;
if ((flags & Snapshot) !== NoFlags)
// 内部调用`getSnapshotBeforeUpdate`生命周期钩子
commitBeforeMutationEffectOnFiber(current, nextEffect);
}
// 如果存在 useEffect 依赖发生更改的 对应的tag,调度 useffect 回调
if ((flags & Passive) !== NoFlags) {
if (!rootDoesHavePassiveEffects) {
rootDoesHavePassiveEffects = true;
// 调度 useEffect 的 hook 执行
scheduleCallback(NormalSchedulerPriority, () => {
// 在commit阶段之后 触发 useEffect
flushPassiveEffects();
return null;
});
}
}
// commit 阶段是同步执行的,useEffect 以 Normal 优先级进行调度
nextEffect = nextEffect.nextEffect;
}
}
commitBeforeMutationEffectOnFiber
js
function commitBeforeMutationLifeCycles(current, finishedWork) {
// 引入别名commitBeforeMutationEffectOnFiber
switch (finishedWork.tag) {
// 类组件
case ClassComponent: {
if (finishedWork.flags & Snapshot) {
if (current !== null) {
const prevProps = current.memoizedProps;
const prevState = current.memoizedState;
// ReactComponent实例
const instance = finishedWork.stateNode;
// 调用实例的 getSnapshotBeforeUpdate
// 此时页面还没有更新
const snapshot = instance.getSnapshotBeforeUpdate(
finishedWork.elementType === finishedWork.type
? prevProps
: resolveDefaultProps(finishedWork.type, prevProps),
prevState,
);
}
}
}
}
}
useEffect 如何异步调度
before mutation阶段
在scheduleCallback
中调度flushPassiveEffects
(未调用)layout阶段
之后将effectList
赋值给rootWithPendingPassiveEffects
scheduleCallback
触发flushPassiveEffects
,flushPassiveEffects
内部遍历rootWithPendingPassiveEffects
为什么需要异步调用
执行时机:
与
componentDidMount
、componentDidUpdate
不同的是,在浏览器完成布局与绘制之后,传给 useEffect 的函数会延迟调用。这使得它适用于许多常见的副作用场景,比如设置订阅和事件处理等情况,因此不应在函数中执行阻塞浏览器更新屏幕的操作。
可见,useEffect
异步执行的原因主要是,防止同步执行时,阻塞浏览器渲染