跳到主要内容

React Fiber 回溯工作

一、作用

二、解除工作

备注
function unwindWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
// Note: This intentionally doesn't check if we're hydrating because comparing
// to the current tree provider fiber is just as fast and less error-prone.
// Ideally we would have a special version of the work loop only
// for hydration.
//
// 注意:这里故意没有检查我们是否在进行 hydration,因为与当前的 tree provider fiber 比较
// 一样快,并且错误更少。
// 理想情况下,我们会有一个专门用于 hydration 的工作循环版本。
popTreeContext(workInProgress);
switch (workInProgress.tag) {
case ClassComponent: {
const Component = workInProgress.type;
if (isLegacyContextProvider(Component)) {
popLegacyContext(workInProgress);
}
const flags = workInProgress.flags;
if (flags & ShouldCapture) {
workInProgress.flags = (flags & ~ShouldCapture) | DidCapture;
if (
enableProfilerTimer &&
(workInProgress.mode & ProfileMode) !== NoMode
) {
transferActualDuration(workInProgress);
}
return workInProgress;
}
return null;
}
case HostRoot: {
const root: FiberRoot = workInProgress.stateNode;
const cache: Cache = workInProgress.memoizedState.cache;
popCacheProvider(workInProgress, cache);

if (enableTransitionTracing) {
popRootMarkerInstance(workInProgress);
}

popRootTransition(workInProgress, root, renderLanes);
popHostContext(workInProgress);
popTopLevelLegacyContextObject(workInProgress);
const flags = workInProgress.flags;
if (
(flags & ShouldCapture) !== NoFlags &&
(flags & DidCapture) === NoFlags
) {
// There was an error during render that wasn't captured by a suspense
// boundary. Do a second pass on the root to unmount the children.
//
// 渲染期间发生了错误,但未被 suspense 边界捕获。
// 对根节点进行第二次渲染以卸载子节点。
workInProgress.flags = (flags & ~ShouldCapture) | DidCapture;
return workInProgress;
}
// We unwound to the root without completing it. Exit.
// 我们回退到了根节点,但未完成它。退出。
return null;
}
case HostHoistable:
case HostSingleton:
case HostComponent: {
// TODO: popHydrationState
// 待办:popHydrationState
popHostContext(workInProgress);
return null;
}
case ActivityComponent: {
const activityState: null | ActivityState = workInProgress.memoizedState;
if (activityState !== null) {
popSuspenseHandler(workInProgress);

if (workInProgress.alternate === null) {
throw new Error(
'Threw in newly mounted dehydrated component. This is likely a bug in ' +
'React. Please file an issue.',
);
}

resetHydrationState();
}

const flags = workInProgress.flags;
if (flags & ShouldCapture) {
workInProgress.flags = (flags & ~ShouldCapture) | DidCapture;
// Captured a suspense effect. Re-render the boundary.
// 捕捉到一个挂起效果。重新渲染边界。
if (
enableProfilerTimer &&
(workInProgress.mode & ProfileMode) !== NoMode
) {
transferActualDuration(workInProgress);
}
return workInProgress;
}
return null;
}
case SuspenseComponent: {
popSuspenseHandler(workInProgress);
const suspenseState: null | SuspenseState = workInProgress.memoizedState;
if (suspenseState !== null && suspenseState.dehydrated !== null) {
if (workInProgress.alternate === null) {
throw new Error(
'Threw in newly mounted dehydrated component. This is likely a bug in ' +
'React. Please file an issue.',
);
}

resetHydrationState();
}

const flags = workInProgress.flags;
if (flags & ShouldCapture) {
workInProgress.flags = (flags & ~ShouldCapture) | DidCapture;
// Captured a suspense effect. Re-render the boundary.
// 捕捉到一个挂起效果。重新渲染边界。
if (
enableProfilerTimer &&
(workInProgress.mode & ProfileMode) !== NoMode
) {
transferActualDuration(workInProgress);
}
return workInProgress;
}
return null;
}
case SuspenseListComponent: {
popSuspenseListContext(workInProgress);
// SuspenseList doesn't normally catch anything. It should've been
// caught by a nested boundary. If not, it should bubble through.
//
// SuspenseList 通常不会捕获任何东西。它本应该被嵌套的边界捕获。
// 如果没有,它应该向上传递。
const flags = workInProgress.flags;
if (flags & ShouldCapture) {
workInProgress.flags = (flags & ~ShouldCapture) | DidCapture;
// If we caught something on the SuspenseList itself it's because
// we want to ignore something. Re-enter the cycle and handle it
// in the complete phase.
//
// 如果我们在 SuspenseList 本身捕获到某些东西,那是因为
// 我们想要忽略它。重新进入循环并在完成阶段处理它。
const renderState: null | SuspenseListRenderState =
workInProgress.memoizedState;
if (renderState !== null) {
// Cut off any remaining tail work and don't commit the rendering one.
// This assumes that we have already confirmed that none of these are
// already mounted.
//
// 剪掉任何剩余的尾部工作,且不要提交渲染部分。
// 这假设我们已经确认这些都没有被挂载。
renderState.rendering = null;
renderState.tail = null;
}
// Schedule the commit phase to attach retry listeners.
// 调度提交阶段以附加重试监听器。
workInProgress.flags |= Update;
return workInProgress;
}
return null;
}
case HostPortal:
popHostContainer(workInProgress);
return null;
case ContextProvider:
const context: ReactContext<any> = workInProgress.type;
popProvider(context, workInProgress);
return null;
case OffscreenComponent:
case LegacyHiddenComponent: {
popSuspenseHandler(workInProgress);
popHiddenContext(workInProgress);
popTransition(workInProgress, current);
const flags = workInProgress.flags;
if (flags & ShouldCapture) {
workInProgress.flags = (flags & ~ShouldCapture) | DidCapture;
// Captured a suspense effect. Re-render the boundary.
// 捕捉到挂起效果。重新渲染边界。
if (
enableProfilerTimer &&
(workInProgress.mode & ProfileMode) !== NoMode
) {
transferActualDuration(workInProgress);
}
return workInProgress;
}
return null;
}
case CacheComponent:
const cache: Cache = workInProgress.memoizedState.cache;
popCacheProvider(workInProgress, cache);
return null;
case TracingMarkerComponent:
if (enableTransitionTracing) {
if (workInProgress.stateNode !== null) {
popMarkerInstance(workInProgress);
}
}
return null;
default:
return null;
}
}

三、中断工作撤销

备注
function unwindInterruptedWork(
current: Fiber | null,
interruptedWork: Fiber,
renderLanes: Lanes,
) {
// Note: This intentionally doesn't check if we're hydrating because comparing
// to the current tree provider fiber is just as fast and less error-prone.
// Ideally we would have a special version of the work loop only
// for hydration.
//
// 注意:这里故意没有检查我们是否在进行 hydration,因为与当前的
// tree provider fiber 比较一样快,并且错误更少。
// 理想情况下,我们会有一个专门用于 hydration 的工作循环版本。
popTreeContext(interruptedWork);
switch (interruptedWork.tag) {
case ClassComponent: {
const childContextTypes = interruptedWork.type.childContextTypes;
if (childContextTypes !== null && childContextTypes !== undefined) {
popLegacyContext(interruptedWork);
}
break;
}
case HostRoot: {
const root: FiberRoot = interruptedWork.stateNode;
const cache: Cache = interruptedWork.memoizedState.cache;
popCacheProvider(interruptedWork, cache);

if (enableTransitionTracing) {
popRootMarkerInstance(interruptedWork);
}

popRootTransition(interruptedWork, root, renderLanes);
popHostContainer(interruptedWork);
popTopLevelLegacyContextObject(interruptedWork);
break;
}
case HostHoistable:
case HostSingleton:
case HostComponent: {
popHostContext(interruptedWork);
break;
}
case HostPortal:
popHostContainer(interruptedWork);
break;
case ActivityComponent: {
if (interruptedWork.memoizedState !== null) {
popSuspenseHandler(interruptedWork);
}
break;
}
case SuspenseComponent:
popSuspenseHandler(interruptedWork);
break;
case SuspenseListComponent:
popSuspenseListContext(interruptedWork);
break;
case ContextProvider:
const context: ReactContext<any> = interruptedWork.type;
popProvider(context, interruptedWork);
break;
case OffscreenComponent:
case LegacyHiddenComponent:
popSuspenseHandler(interruptedWork);
popHiddenContext(interruptedWork);
popTransition(interruptedWork, current);
break;
case CacheComponent:
const cache: Cache = interruptedWork.memoizedState.cache;
popCacheProvider(interruptedWork, cache);
break;
case TracingMarkerComponent:
if (enableTransitionTracing) {
const instance: TracingMarkerInstance | null =
interruptedWork.stateNode;
if (instance !== null) {
popMarkerInstance(interruptedWork);
}
}
break;
default:
break;
}
}