commitBeforeMutationLifecycles
1 )概述
- 在 react commit 阶段的
commitRoot
第一个while循环中 - 调用了
commitBeforeMutationLifeCycles
- 现在来看下,里面发生了什么
2 )源码
回到 commit 阶段的第一个循环中,在 commitRoot 函数里
定位到 packages/react-reconciler/src/ReactFiberScheduler.js#L647
看这里
// Invoke instances of getSnapshotBeforeUpdate before mutation.
nextEffect = firstEffect;
startCommitSnapshotEffectsTimer();
// 接下去就进入了三个循环:
// 第一个循环主要是 commitBeforeMutationLifecycles,这个方法它其实很简单,它是这三个循环当中最简单的一个方法
// 它唯一的作用就是调用 ClassComponent 上面可能会存在的 getSnapshotBeforeUpdate 这么一个生命周期方法
while (nextEffect !== null) {
let didError = false;
let error;
if (__DEV__) {
invokeGuardedCallback(null, commitBeforeMutationLifecycles, null);
if (hasCaughtError()) {
didError = true;
error = clearCaughtError();
}
} else {
try {
commitBeforeMutationLifecycles();
} catch (e) {
didError = true;
error = e;
}
}
if (didError) {
invariant(
nextEffect !== null,
'Should have next effect. This error is likely caused by a bug ' +
'in React. Please file an issue.',
);
captureCommitPhaseError(nextEffect, error);
// Clean-up
if (nextEffect !== null) {
nextEffect = nextEffect.nextEffect;
}
}
}
- 在开始循环之前都会用到一个全局变量叫做 nextEffect,当然它一开始是 firstEffect
- 是在 RootFiber 上面,它的 firstEffect 到 lastEffect 这个链表上面的第一个effect
- 也就是第一个需要我们commit的fiber对象
- 需要注意一点,这边有一个很奇怪的代码,就是我们这个
commitBeforeMutationLifecycles
方法- 可以看到
import { commitBeforeMutationLifeCycles } from './ReactFiberCommitWork'
- 但是又在本文件中,定义了一个同名的方法
// 这个是核心 function commitBeforeMutationLifecycles() { while (nextEffect !== null) { if (__DEV__) { ReactCurrentFiber.setCurrentFiber(nextEffect); } const effectTag = nextEffect.effectTag; if (effectTag & Snapshot) { recordEffect(); const current = nextEffect.alternate; commitBeforeMutationLifeCycles(current, nextEffect); // 这个函数,不是上层的自己,而是 import 进来的 } nextEffect = nextEffect.nextEffect; } if (__DEV__) { ReactCurrentFiber.resetCurrentFiber(); } }
- 可以看到
- 这个操作会有点奇怪,因为正常来讲,import 进来的内容,它是一个常量,它不应该是能够直接被修改
- 注意,在 commitRoot 里调用的
commitBeforeMutationLifeCycles
是文件内的这个方法 - 而文件内的这个方法,调用的
commitBeforeMutationLifeCycles
是 import 进来的- 它来自于 packages/react-reconciler/src/ReactFiberCommitWork.js#L215
- 它通过一个while循环,循环的是我们的 effect链
- 在这个循环的后面,可以看到
nextEffect = nextEffect.nextEffect;
- 在这个循环的后面,可以看到
- 这就是说一个一个往下这么去循环,对 有
Snapshot
这个 SideEffect 的节点执行了下面的方法
再次定位到 packages/react-reconciler/src/ReactFiberCommitWork.js#L215
进入 commitBeforeMutationLifeCycles
这个就是上面定义的同名函数里面调用的方法
function commitBeforeMutationLifeCycles(
current: Fiber | null,
finishedWork: Fiber,
): void {
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
commitHookEffectList(UnmountSnapshot, NoHookEffect, finishedWork);
return;
}
case ClassComponent: {
if (finishedWork.effectTag & Snapshot) {
if (current !== null) {
const prevProps = current.memoizedProps;
const prevState = current.memoizedState;
startPhaseTimer(finishedWork, 'getSnapshotBeforeUpdate');
const instance = finishedWork.stateNode;
// 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.
if (__DEV__) {
if (finishedWork.type === finishedWork.elementType) {
warning(
instance.props === finishedWork.memoizedProps,
'Expected instance props to match memoized props before ' +
'getSnapshotBeforeUpdate. This is likely due to a bug in React. ' +
'Please file an issue.',
);
warning(
instance.state === finishedWork.memoizedState,
'Expected instance state to match memoized state before ' +
'getSnapshotBeforeUpdate. This is likely due to a bug in React. ' +
'Please file an issue.',
);
}
}
const snapshot = instance.getSnapshotBeforeUpdate(
finishedWork.elementType === finishedWork.type
? prevProps
: resolveDefaultProps(finishedWork.type, prevProps),
prevState,
);
if (__DEV__) {
const didWarnSet = ((didWarnAboutUndefinedSnapshotBeforeUpdate: any): Set<
mixed,
>);
if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) {
didWarnSet.add(finishedWork.type);
warningWithoutStack(
false,
'%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' +
'must be returned. You have returned undefined.',
getComponentName(finishedWork.type),
);
}
}
instance.__reactInternalSnapshotBeforeUpdate = snapshot;
stopPhaseTimer();
}
}
return;
}
case HostRoot:
case HostComponent:
case HostText:
case HostPortal:
case IncompleteClassComponent:
// Nothing to do for these component types
return;
default: {
invariant(
false,
'This unit of work tag should not have side-effects. This error is ' +
'likely caused by a bug in React. Please file an issue.',
);
}
}
}
- 它传入的 current 是 workInProgress 对象,也就是fiber对象
- 然后传入的 nextEfect 是 finishedWork 对象
- 通过
instance.getSnapshotBeforeUpdate
传入两个 props 来获取snapshot
快照对象 - 之后将
snapshot
挂载到instance.__reactInternalSnapshotBeforeUpdate
- 对于其他类型的组件,它都没有任何的处理,只处理了 ClassComponent
- 因为对这种情况来说,只有 ClassComponent 有这个 SideEffect
- 并且会有
getSnapshotBeforeUpdate
这个生命周期方法的