Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 34308b5

Browse files
authoredJul 29, 2021
Tidy up early bailout logic at start of begin phase (#21852)
* Extract early bailout to separate function This block is getting hard to read so I moved it to a separate function. I'm about to refactor the logic that wraps around this path. Ideally this early bailout path would happen before the begin phase phase. Perhaps during reconcilation of the parent fiber's children. * Extract state and context check to separate function The only reason we pass `updateLanes` to some begin functions is to check if we can perform an early bail out. But this is also available as `current.lanes`, so we can read it from there instead. I think the only reason we didn't do it this way originally is because components that have two phases — error and Suspense boundaries — use `workInProgress.lanes` to prevent a bail out, since during the initial render there is no `current`. But we can check the `DidCapture` flag instead, which we use elsewhere to detect the second phase.
1 parent d9dd965 commit 34308b5

File tree

4 files changed

+520
-490
lines changed

4 files changed

+520
-490
lines changed
 

‎packages/react-reconciler/src/ReactFiberBeginWork.new.js

Lines changed: 258 additions & 245 deletions
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,6 @@ function updateMemoComponent(
414414
workInProgress: Fiber,
415415
Component: any,
416416
nextProps: any,
417-
updateLanes: Lanes,
418417
renderLanes: Lanes,
419418
): null | Fiber {
420419
if (current === null) {
@@ -442,7 +441,6 @@ function updateMemoComponent(
442441
workInProgress,
443442
resolvedType,
444443
nextProps,
445-
updateLanes,
446444
renderLanes,
447445
);
448446
}
@@ -487,7 +485,11 @@ function updateMemoComponent(
487485
}
488486
}
489487
const currentChild = ((current.child: any): Fiber); // This is always exactly one child
490-
if (!includesSomeLane(updateLanes, renderLanes)) {
488+
const hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(
489+
current,
490+
renderLanes,
491+
);
492+
if (!hasScheduledUpdateOrContext) {
491493
// This will be the props with resolved defaultProps,
492494
// unlike current.memoizedProps which will be the unresolved ones.
493495
const prevProps = currentChild.memoizedProps;
@@ -512,7 +514,6 @@ function updateSimpleMemoComponent(
512514
workInProgress: Fiber,
513515
Component: any,
514516
nextProps: any,
515-
updateLanes: Lanes,
516517
renderLanes: Lanes,
517518
): null | Fiber {
518519
// TODO: current can be non-null here even if the component
@@ -558,7 +559,7 @@ function updateSimpleMemoComponent(
558559
(__DEV__ ? workInProgress.type === current.type : true)
559560
) {
560561
didReceiveUpdate = false;
561-
if (!includesSomeLane(renderLanes, updateLanes)) {
562+
if (!checkScheduledUpdateOrContext(current, renderLanes)) {
562563
// The pending lanes were cleared at the beginning of beginWork. We're
563564
// about to bail out, but there might be other lanes that weren't
564565
// included in the current render. Usually, the priority level of the
@@ -806,7 +807,6 @@ const updateLegacyHiddenComponent = updateOffscreenComponent;
806807
function updateCacheComponent(
807808
current: Fiber | null,
808809
workInProgress: Fiber,
809-
updateLanes: Lanes,
810810
renderLanes: Lanes,
811811
) {
812812
if (!enableCache) {
@@ -828,7 +828,7 @@ function updateCacheComponent(
828828
pushCacheProvider(workInProgress, freshCache);
829829
} else {
830830
// Check for updates
831-
if (includesSomeLane(renderLanes, updateLanes)) {
831+
if (includesSomeLane(current.lanes, renderLanes)) {
832832
cloneUpdateQueue(current, workInProgress);
833833
processUpdateQueue(workInProgress, null, null, renderLanes);
834834
}
@@ -1372,7 +1372,6 @@ function mountLazyComponent(
13721372
_current,
13731373
workInProgress,
13741374
elementType,
1375-
updateLanes,
13761375
renderLanes,
13771376
) {
13781377
if (_current !== null) {
@@ -1462,7 +1461,6 @@ function mountLazyComponent(
14621461
workInProgress,
14631462
Component,
14641463
resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too
1465-
updateLanes,
14661464
renderLanes,
14671465
);
14681466
return child;
@@ -3347,13 +3345,241 @@ function remountFiber(
33473345
}
33483346
}
33493347

3348+
function checkScheduledUpdateOrContext(
3349+
current: Fiber,
3350+
renderLanes: Lanes,
3351+
): boolean {
3352+
// Before performing an early bailout, we must check if there are pending
3353+
// updates or context.
3354+
const updateLanes = current.lanes;
3355+
if (includesSomeLane(updateLanes, renderLanes)) {
3356+
return true;
3357+
}
3358+
// No pending update, but because context is propagated lazily, we need
3359+
// to check for a context change before we bail out.
3360+
if (enableLazyContextPropagation) {
3361+
const dependencies = current.dependencies;
3362+
if (dependencies !== null && checkIfContextChanged(dependencies)) {
3363+
return true;
3364+
}
3365+
}
3366+
return false;
3367+
}
3368+
3369+
function attemptEarlyBailoutIfNoScheduledUpdate(
3370+
current: Fiber,
3371+
workInProgress: Fiber,
3372+
renderLanes: Lanes,
3373+
) {
3374+
// This fiber does not have any pending work. Bailout without entering
3375+
// the begin phase. There's still some bookkeeping we that needs to be done
3376+
// in this optimized path, mostly pushing stuff onto the stack.
3377+
switch (workInProgress.tag) {
3378+
case HostRoot:
3379+
pushHostRootContext(workInProgress);
3380+
if (enableCache) {
3381+
const root: FiberRoot = workInProgress.stateNode;
3382+
const cache: Cache = current.memoizedState.cache;
3383+
pushCacheProvider(workInProgress, cache);
3384+
pushRootCachePool(root);
3385+
}
3386+
resetHydrationState();
3387+
break;
3388+
case HostComponent:
3389+
pushHostContext(workInProgress);
3390+
break;
3391+
case ClassComponent: {
3392+
const Component = workInProgress.type;
3393+
if (isLegacyContextProvider(Component)) {
3394+
pushLegacyContextProvider(workInProgress);
3395+
}
3396+
break;
3397+
}
3398+
case HostPortal:
3399+
pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
3400+
break;
3401+
case ContextProvider: {
3402+
const newValue = workInProgress.memoizedProps.value;
3403+
const context: ReactContext<any> = workInProgress.type._context;
3404+
pushProvider(workInProgress, context, newValue);
3405+
break;
3406+
}
3407+
case Profiler:
3408+
if (enableProfilerTimer) {
3409+
// Profiler should only call onRender when one of its descendants actually rendered.
3410+
const hasChildWork = includesSomeLane(
3411+
renderLanes,
3412+
workInProgress.childLanes,
3413+
);
3414+
if (hasChildWork) {
3415+
workInProgress.flags |= Update;
3416+
}
3417+
3418+
if (enableProfilerCommitHooks) {
3419+
// Reset effect durations for the next eventual effect phase.
3420+
// These are reset during render to allow the DevTools commit hook a chance to read them,
3421+
const stateNode = workInProgress.stateNode;
3422+
stateNode.effectDuration = 0;
3423+
stateNode.passiveEffectDuration = 0;
3424+
}
3425+
}
3426+
break;
3427+
case SuspenseComponent: {
3428+
const state: SuspenseState | null = workInProgress.memoizedState;
3429+
if (state !== null) {
3430+
if (enableSuspenseServerRenderer) {
3431+
if (state.dehydrated !== null) {
3432+
pushSuspenseContext(
3433+
workInProgress,
3434+
setDefaultShallowSuspenseContext(suspenseStackCursor.current),
3435+
);
3436+
// We know that this component will suspend again because if it has
3437+
// been unsuspended it has committed as a resolved Suspense component.
3438+
// If it needs to be retried, it should have work scheduled on it.
3439+
workInProgress.flags |= DidCapture;
3440+
// We should never render the children of a dehydrated boundary until we
3441+
// upgrade it. We return null instead of bailoutOnAlreadyFinishedWork.
3442+
return null;
3443+
}
3444+
}
3445+
3446+
// If this boundary is currently timed out, we need to decide
3447+
// whether to retry the primary children, or to skip over it and
3448+
// go straight to the fallback. Check the priority of the primary
3449+
// child fragment.
3450+
const primaryChildFragment: Fiber = (workInProgress.child: any);
3451+
const primaryChildLanes = primaryChildFragment.childLanes;
3452+
if (includesSomeLane(renderLanes, primaryChildLanes)) {
3453+
// The primary children have pending work. Use the normal path
3454+
// to attempt to render the primary children again.
3455+
return updateSuspenseComponent(current, workInProgress, renderLanes);
3456+
} else {
3457+
// The primary child fragment does not have pending work marked
3458+
// on it
3459+
pushSuspenseContext(
3460+
workInProgress,
3461+
setDefaultShallowSuspenseContext(suspenseStackCursor.current),
3462+
);
3463+
// The primary children do not have pending work with sufficient
3464+
// priority. Bailout.
3465+
const child = bailoutOnAlreadyFinishedWork(
3466+
current,
3467+
workInProgress,
3468+
renderLanes,
3469+
);
3470+
if (child !== null) {
3471+
// The fallback children have pending work. Skip over the
3472+
// primary children and work on the fallback.
3473+
return child.sibling;
3474+
} else {
3475+
// Note: We can return `null` here because we already checked
3476+
// whether there were nested context consumers, via the call to
3477+
// `bailoutOnAlreadyFinishedWork` above.
3478+
return null;
3479+
}
3480+
}
3481+
} else {
3482+
pushSuspenseContext(
3483+
workInProgress,
3484+
setDefaultShallowSuspenseContext(suspenseStackCursor.current),
3485+
);
3486+
}
3487+
break;
3488+
}
3489+
case SuspenseListComponent: {
3490+
const didSuspendBefore = (current.flags & DidCapture) !== NoFlags;
3491+
3492+
let hasChildWork = includesSomeLane(
3493+
renderLanes,
3494+
workInProgress.childLanes,
3495+
);
3496+
3497+
if (enableLazyContextPropagation && !hasChildWork) {
3498+
// Context changes may not have been propagated yet. We need to do
3499+
// that now, before we can decide whether to bail out.
3500+
// TODO: We use `childLanes` as a heuristic for whether there is
3501+
// remaining work in a few places, including
3502+
// `bailoutOnAlreadyFinishedWork` and
3503+
// `updateDehydratedSuspenseComponent`. We should maybe extract this
3504+
// into a dedicated function.
3505+
lazilyPropagateParentContextChanges(
3506+
current,
3507+
workInProgress,
3508+
renderLanes,
3509+
);
3510+
hasChildWork = includesSomeLane(renderLanes, workInProgress.childLanes);
3511+
}
3512+
3513+
if (didSuspendBefore) {
3514+
if (hasChildWork) {
3515+
// If something was in fallback state last time, and we have all the
3516+
// same children then we're still in progressive loading state.
3517+
// Something might get unblocked by state updates or retries in the
3518+
// tree which will affect the tail. So we need to use the normal
3519+
// path to compute the correct tail.
3520+
return updateSuspenseListComponent(
3521+
current,
3522+
workInProgress,
3523+
renderLanes,
3524+
);
3525+
}
3526+
// If none of the children had any work, that means that none of
3527+
// them got retried so they'll still be blocked in the same way
3528+
// as before. We can fast bail out.
3529+
workInProgress.flags |= DidCapture;
3530+
}
3531+
3532+
// If nothing suspended before and we're rendering the same children,
3533+
// then the tail doesn't matter. Anything new that suspends will work
3534+
// in the "together" mode, so we can continue from the state we had.
3535+
const renderState = workInProgress.memoizedState;
3536+
if (renderState !== null) {
3537+
// Reset to the "together" mode in case we've started a different
3538+
// update in the past but didn't complete it.
3539+
renderState.rendering = null;
3540+
renderState.tail = null;
3541+
renderState.lastEffect = null;
3542+
}
3543+
pushSuspenseContext(workInProgress, suspenseStackCursor.current);
3544+
3545+
if (hasChildWork) {
3546+
break;
3547+
} else {
3548+
// If none of the children had any work, that means that none of
3549+
// them got retried so they'll still be blocked in the same way
3550+
// as before. We can fast bail out.
3551+
return null;
3552+
}
3553+
}
3554+
case OffscreenComponent:
3555+
case LegacyHiddenComponent: {
3556+
// Need to check if the tree still needs to be deferred. This is
3557+
// almost identical to the logic used in the normal update path,
3558+
// so we'll just enter that. The only difference is we'll bail out
3559+
// at the next level instead of this one, because the child props
3560+
// have not changed. Which is fine.
3561+
// TODO: Probably should refactor `beginWork` to split the bailout
3562+
// path from the normal path. I'm tempted to do a labeled break here
3563+
// but I won't :)
3564+
workInProgress.lanes = NoLanes;
3565+
return updateOffscreenComponent(current, workInProgress, renderLanes);
3566+
}
3567+
case CacheComponent: {
3568+
if (enableCache) {
3569+
const cache: Cache = current.memoizedState.cache;
3570+
pushCacheProvider(workInProgress, cache);
3571+
}
3572+
break;
3573+
}
3574+
}
3575+
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
3576+
}
3577+
33503578
function beginWork(
33513579
current: Fiber | null,
33523580
workInProgress: Fiber,
33533581
renderLanes: Lanes,
33543582
): Fiber | null {
3355-
let updateLanes = workInProgress.lanes;
3356-
33573583
if (__DEV__) {
33583584
if (workInProgress._debugNeedsRemount && current !== null) {
33593585
// This will restart the begin phase with a new fiber.
@@ -3373,17 +3599,6 @@ function beginWork(
33733599
}
33743600

33753601
if (current !== null) {
3376-
// TODO: The factoring of this block is weird.
3377-
if (
3378-
enableLazyContextPropagation &&
3379-
!includesSomeLane(renderLanes, updateLanes)
3380-
) {
3381-
const dependencies = current.dependencies;
3382-
if (dependencies !== null && checkIfContextChanged(dependencies)) {
3383-
updateLanes = mergeLanes(updateLanes, renderLanes);
3384-
}
3385-
}
3386-
33873602
const oldProps = current.memoizedProps;
33883603
const newProps = workInProgress.pendingProps;
33893604

@@ -3396,221 +3611,27 @@ function beginWork(
33963611
// If props or context changed, mark the fiber as having performed work.
33973612
// This may be unset if the props are determined to be equal later (memo).
33983613
didReceiveUpdate = true;
3399-
} else if (!includesSomeLane(renderLanes, updateLanes)) {
3400-
didReceiveUpdate = false;
3401-
// This fiber does not have any pending work. Bailout without entering
3402-
// the begin phase. There's still some bookkeeping we that needs to be done
3403-
// in this optimized path, mostly pushing stuff onto the stack.
3404-
switch (workInProgress.tag) {
3405-
case HostRoot:
3406-
pushHostRootContext(workInProgress);
3407-
if (enableCache) {
3408-
const root: FiberRoot = workInProgress.stateNode;
3409-
const cache: Cache = current.memoizedState.cache;
3410-
pushCacheProvider(workInProgress, cache);
3411-
pushRootCachePool(root);
3412-
}
3413-
resetHydrationState();
3414-
break;
3415-
case HostComponent:
3416-
pushHostContext(workInProgress);
3417-
break;
3418-
case ClassComponent: {
3419-
const Component = workInProgress.type;
3420-
if (isLegacyContextProvider(Component)) {
3421-
pushLegacyContextProvider(workInProgress);
3422-
}
3423-
break;
3424-
}
3425-
case HostPortal:
3426-
pushHostContainer(
3427-
workInProgress,
3428-
workInProgress.stateNode.containerInfo,
3429-
);
3430-
break;
3431-
case ContextProvider: {
3432-
const newValue = workInProgress.memoizedProps.value;
3433-
const context: ReactContext<any> = workInProgress.type._context;
3434-
pushProvider(workInProgress, context, newValue);
3435-
break;
3436-
}
3437-
case Profiler:
3438-
if (enableProfilerTimer) {
3439-
// Profiler should only call onRender when one of its descendants actually rendered.
3440-
const hasChildWork = includesSomeLane(
3441-
renderLanes,
3442-
workInProgress.childLanes,
3443-
);
3444-
if (hasChildWork) {
3445-
workInProgress.flags |= Update;
3446-
}
3447-
3448-
if (enableProfilerCommitHooks) {
3449-
// Reset effect durations for the next eventual effect phase.
3450-
// These are reset during render to allow the DevTools commit hook a chance to read them,
3451-
const stateNode = workInProgress.stateNode;
3452-
stateNode.effectDuration = 0;
3453-
stateNode.passiveEffectDuration = 0;
3454-
}
3455-
}
3456-
break;
3457-
case SuspenseComponent: {
3458-
const state: SuspenseState | null = workInProgress.memoizedState;
3459-
if (state !== null) {
3460-
if (enableSuspenseServerRenderer) {
3461-
if (state.dehydrated !== null) {
3462-
pushSuspenseContext(
3463-
workInProgress,
3464-
setDefaultShallowSuspenseContext(suspenseStackCursor.current),
3465-
);
3466-
// We know that this component will suspend again because if it has
3467-
// been unsuspended it has committed as a resolved Suspense component.
3468-
// If it needs to be retried, it should have work scheduled on it.
3469-
workInProgress.flags |= DidCapture;
3470-
// We should never render the children of a dehydrated boundary until we
3471-
// upgrade it. We return null instead of bailoutOnAlreadyFinishedWork.
3472-
return null;
3473-
}
3474-
}
3475-
3476-
// If this boundary is currently timed out, we need to decide
3477-
// whether to retry the primary children, or to skip over it and
3478-
// go straight to the fallback. Check the priority of the primary
3479-
// child fragment.
3480-
const primaryChildFragment: Fiber = (workInProgress.child: any);
3481-
const primaryChildLanes = primaryChildFragment.childLanes;
3482-
if (includesSomeLane(renderLanes, primaryChildLanes)) {
3483-
// The primary children have pending work. Use the normal path
3484-
// to attempt to render the primary children again.
3485-
return updateSuspenseComponent(
3486-
current,
3487-
workInProgress,
3488-
renderLanes,
3489-
);
3490-
} else {
3491-
// The primary child fragment does not have pending work marked
3492-
// on it
3493-
pushSuspenseContext(
3494-
workInProgress,
3495-
setDefaultShallowSuspenseContext(suspenseStackCursor.current),
3496-
);
3497-
// The primary children do not have pending work with sufficient
3498-
// priority. Bailout.
3499-
const child = bailoutOnAlreadyFinishedWork(
3500-
current,
3501-
workInProgress,
3502-
renderLanes,
3503-
);
3504-
if (child !== null) {
3505-
// The fallback children have pending work. Skip over the
3506-
// primary children and work on the fallback.
3507-
return child.sibling;
3508-
} else {
3509-
// Note: We can return `null` here because we already checked
3510-
// whether there were nested context consumers, via the call to
3511-
// `bailoutOnAlreadyFinishedWork` above.
3512-
return null;
3513-
}
3514-
}
3515-
} else {
3516-
pushSuspenseContext(
3517-
workInProgress,
3518-
setDefaultShallowSuspenseContext(suspenseStackCursor.current),
3519-
);
3520-
}
3521-
break;
3522-
}
3523-
case SuspenseListComponent: {
3524-
const didSuspendBefore = (current.flags & DidCapture) !== NoFlags;
3525-
3526-
let hasChildWork = includesSomeLane(
3527-
renderLanes,
3528-
workInProgress.childLanes,
3529-
);
3530-
3531-
if (enableLazyContextPropagation && !hasChildWork) {
3532-
// Context changes may not have been propagated yet. We need to do
3533-
// that now, before we can decide whether to bail out.
3534-
// TODO: We use `childLanes` as a heuristic for whether there is
3535-
// remaining work in a few places, including
3536-
// `bailoutOnAlreadyFinishedWork` and
3537-
// `updateDehydratedSuspenseComponent`. We should maybe extract this
3538-
// into a dedicated function.
3539-
lazilyPropagateParentContextChanges(
3540-
current,
3541-
workInProgress,
3542-
renderLanes,
3543-
);
3544-
hasChildWork = includesSomeLane(
3545-
renderLanes,
3546-
workInProgress.childLanes,
3547-
);
3548-
}
3549-
3550-
if (didSuspendBefore) {
3551-
if (hasChildWork) {
3552-
// If something was in fallback state last time, and we have all the
3553-
// same children then we're still in progressive loading state.
3554-
// Something might get unblocked by state updates or retries in the
3555-
// tree which will affect the tail. So we need to use the normal
3556-
// path to compute the correct tail.
3557-
return updateSuspenseListComponent(
3558-
current,
3559-
workInProgress,
3560-
renderLanes,
3561-
);
3562-
}
3563-
// If none of the children had any work, that means that none of
3564-
// them got retried so they'll still be blocked in the same way
3565-
// as before. We can fast bail out.
3566-
workInProgress.flags |= DidCapture;
3567-
}
3568-
3569-
// If nothing suspended before and we're rendering the same children,
3570-
// then the tail doesn't matter. Anything new that suspends will work
3571-
// in the "together" mode, so we can continue from the state we had.
3572-
const renderState = workInProgress.memoizedState;
3573-
if (renderState !== null) {
3574-
// Reset to the "together" mode in case we've started a different
3575-
// update in the past but didn't complete it.
3576-
renderState.rendering = null;
3577-
renderState.tail = null;
3578-
renderState.lastEffect = null;
3579-
}
3580-
pushSuspenseContext(workInProgress, suspenseStackCursor.current);
3581-
3582-
if (hasChildWork) {
3583-
break;
3584-
} else {
3585-
// If none of the children had any work, that means that none of
3586-
// them got retried so they'll still be blocked in the same way
3587-
// as before. We can fast bail out.
3588-
return null;
3589-
}
3590-
}
3591-
case OffscreenComponent:
3592-
case LegacyHiddenComponent: {
3593-
// Need to check if the tree still needs to be deferred. This is
3594-
// almost identical to the logic used in the normal update path,
3595-
// so we'll just enter that. The only difference is we'll bail out
3596-
// at the next level instead of this one, because the child props
3597-
// have not changed. Which is fine.
3598-
// TODO: Probably should refactor `beginWork` to split the bailout
3599-
// path from the normal path. I'm tempted to do a labeled break here
3600-
// but I won't :)
3601-
workInProgress.lanes = NoLanes;
3602-
return updateOffscreenComponent(current, workInProgress, renderLanes);
3603-
}
3604-
case CacheComponent: {
3605-
if (enableCache) {
3606-
const cache: Cache = current.memoizedState.cache;
3607-
pushCacheProvider(workInProgress, cache);
3608-
}
3609-
break;
3610-
}
3611-
}
3612-
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
36133614
} else {
3615+
// Neither props nor legacy context changes. Check if there's a pending
3616+
// update or context change.
3617+
const hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(
3618+
current,
3619+
renderLanes,
3620+
);
3621+
if (
3622+
!hasScheduledUpdateOrContext &&
3623+
// If this is the second pass of an error or suspense boundary, there
3624+
// may not be work scheduled on `current`, so we check for this flag.
3625+
(workInProgress.flags & DidCapture) === NoFlags
3626+
) {
3627+
// No pending updates or context. Bail out now.
3628+
didReceiveUpdate = false;
3629+
return attemptEarlyBailoutIfNoScheduledUpdate(
3630+
current,
3631+
workInProgress,
3632+
renderLanes,
3633+
);
3634+
}
36143635
if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) {
36153636
// This is a special case that only exists for legacy mode.
36163637
// See https://github.com/facebook/react/pull/19216.
@@ -3649,7 +3670,6 @@ function beginWork(
36493670
current,
36503671
workInProgress,
36513672
elementType,
3652-
updateLanes,
36533673
renderLanes,
36543674
);
36553675
}
@@ -3742,7 +3762,6 @@ function beginWork(
37423762
workInProgress,
37433763
type,
37443764
resolvedProps,
3745-
updateLanes,
37463765
renderLanes,
37473766
);
37483767
}
@@ -3752,7 +3771,6 @@ function beginWork(
37523771
workInProgress,
37533772
workInProgress.type,
37543773
workInProgress.pendingProps,
3755-
updateLanes,
37563774
renderLanes,
37573775
);
37583776
}
@@ -3788,12 +3806,7 @@ function beginWork(
37883806
}
37893807
case CacheComponent: {
37903808
if (enableCache) {
3791-
return updateCacheComponent(
3792-
current,
3793-
workInProgress,
3794-
updateLanes,
3795-
renderLanes,
3796-
);
3809+
return updateCacheComponent(current, workInProgress, renderLanes);
37973810
}
37983811
break;
37993812
}

‎packages/react-reconciler/src/ReactFiberBeginWork.old.js

Lines changed: 258 additions & 245 deletions
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,6 @@ function updateMemoComponent(
414414
workInProgress: Fiber,
415415
Component: any,
416416
nextProps: any,
417-
updateLanes: Lanes,
418417
renderLanes: Lanes,
419418
): null | Fiber {
420419
if (current === null) {
@@ -442,7 +441,6 @@ function updateMemoComponent(
442441
workInProgress,
443442
resolvedType,
444443
nextProps,
445-
updateLanes,
446444
renderLanes,
447445
);
448446
}
@@ -487,7 +485,11 @@ function updateMemoComponent(
487485
}
488486
}
489487
const currentChild = ((current.child: any): Fiber); // This is always exactly one child
490-
if (!includesSomeLane(updateLanes, renderLanes)) {
488+
const hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(
489+
current,
490+
renderLanes,
491+
);
492+
if (!hasScheduledUpdateOrContext) {
491493
// This will be the props with resolved defaultProps,
492494
// unlike current.memoizedProps which will be the unresolved ones.
493495
const prevProps = currentChild.memoizedProps;
@@ -512,7 +514,6 @@ function updateSimpleMemoComponent(
512514
workInProgress: Fiber,
513515
Component: any,
514516
nextProps: any,
515-
updateLanes: Lanes,
516517
renderLanes: Lanes,
517518
): null | Fiber {
518519
// TODO: current can be non-null here even if the component
@@ -558,7 +559,7 @@ function updateSimpleMemoComponent(
558559
(__DEV__ ? workInProgress.type === current.type : true)
559560
) {
560561
didReceiveUpdate = false;
561-
if (!includesSomeLane(renderLanes, updateLanes)) {
562+
if (!checkScheduledUpdateOrContext(current, renderLanes)) {
562563
// The pending lanes were cleared at the beginning of beginWork. We're
563564
// about to bail out, but there might be other lanes that weren't
564565
// included in the current render. Usually, the priority level of the
@@ -806,7 +807,6 @@ const updateLegacyHiddenComponent = updateOffscreenComponent;
806807
function updateCacheComponent(
807808
current: Fiber | null,
808809
workInProgress: Fiber,
809-
updateLanes: Lanes,
810810
renderLanes: Lanes,
811811
) {
812812
if (!enableCache) {
@@ -828,7 +828,7 @@ function updateCacheComponent(
828828
pushCacheProvider(workInProgress, freshCache);
829829
} else {
830830
// Check for updates
831-
if (includesSomeLane(renderLanes, updateLanes)) {
831+
if (includesSomeLane(current.lanes, renderLanes)) {
832832
cloneUpdateQueue(current, workInProgress);
833833
processUpdateQueue(workInProgress, null, null, renderLanes);
834834
}
@@ -1372,7 +1372,6 @@ function mountLazyComponent(
13721372
_current,
13731373
workInProgress,
13741374
elementType,
1375-
updateLanes,
13761375
renderLanes,
13771376
) {
13781377
if (_current !== null) {
@@ -1462,7 +1461,6 @@ function mountLazyComponent(
14621461
workInProgress,
14631462
Component,
14641463
resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too
1465-
updateLanes,
14661464
renderLanes,
14671465
);
14681466
return child;
@@ -3347,13 +3345,241 @@ function remountFiber(
33473345
}
33483346
}
33493347

3348+
function checkScheduledUpdateOrContext(
3349+
current: Fiber,
3350+
renderLanes: Lanes,
3351+
): boolean {
3352+
// Before performing an early bailout, we must check if there are pending
3353+
// updates or context.
3354+
const updateLanes = current.lanes;
3355+
if (includesSomeLane(updateLanes, renderLanes)) {
3356+
return true;
3357+
}
3358+
// No pending update, but because context is propagated lazily, we need
3359+
// to check for a context change before we bail out.
3360+
if (enableLazyContextPropagation) {
3361+
const dependencies = current.dependencies;
3362+
if (dependencies !== null && checkIfContextChanged(dependencies)) {
3363+
return true;
3364+
}
3365+
}
3366+
return false;
3367+
}
3368+
3369+
function attemptEarlyBailoutIfNoScheduledUpdate(
3370+
current: Fiber,
3371+
workInProgress: Fiber,
3372+
renderLanes: Lanes,
3373+
) {
3374+
// This fiber does not have any pending work. Bailout without entering
3375+
// the begin phase. There's still some bookkeeping we that needs to be done
3376+
// in this optimized path, mostly pushing stuff onto the stack.
3377+
switch (workInProgress.tag) {
3378+
case HostRoot:
3379+
pushHostRootContext(workInProgress);
3380+
if (enableCache) {
3381+
const root: FiberRoot = workInProgress.stateNode;
3382+
const cache: Cache = current.memoizedState.cache;
3383+
pushCacheProvider(workInProgress, cache);
3384+
pushRootCachePool(root);
3385+
}
3386+
resetHydrationState();
3387+
break;
3388+
case HostComponent:
3389+
pushHostContext(workInProgress);
3390+
break;
3391+
case ClassComponent: {
3392+
const Component = workInProgress.type;
3393+
if (isLegacyContextProvider(Component)) {
3394+
pushLegacyContextProvider(workInProgress);
3395+
}
3396+
break;
3397+
}
3398+
case HostPortal:
3399+
pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
3400+
break;
3401+
case ContextProvider: {
3402+
const newValue = workInProgress.memoizedProps.value;
3403+
const context: ReactContext<any> = workInProgress.type._context;
3404+
pushProvider(workInProgress, context, newValue);
3405+
break;
3406+
}
3407+
case Profiler:
3408+
if (enableProfilerTimer) {
3409+
// Profiler should only call onRender when one of its descendants actually rendered.
3410+
const hasChildWork = includesSomeLane(
3411+
renderLanes,
3412+
workInProgress.childLanes,
3413+
);
3414+
if (hasChildWork) {
3415+
workInProgress.flags |= Update;
3416+
}
3417+
3418+
if (enableProfilerCommitHooks) {
3419+
// Reset effect durations for the next eventual effect phase.
3420+
// These are reset during render to allow the DevTools commit hook a chance to read them,
3421+
const stateNode = workInProgress.stateNode;
3422+
stateNode.effectDuration = 0;
3423+
stateNode.passiveEffectDuration = 0;
3424+
}
3425+
}
3426+
break;
3427+
case SuspenseComponent: {
3428+
const state: SuspenseState | null = workInProgress.memoizedState;
3429+
if (state !== null) {
3430+
if (enableSuspenseServerRenderer) {
3431+
if (state.dehydrated !== null) {
3432+
pushSuspenseContext(
3433+
workInProgress,
3434+
setDefaultShallowSuspenseContext(suspenseStackCursor.current),
3435+
);
3436+
// We know that this component will suspend again because if it has
3437+
// been unsuspended it has committed as a resolved Suspense component.
3438+
// If it needs to be retried, it should have work scheduled on it.
3439+
workInProgress.flags |= DidCapture;
3440+
// We should never render the children of a dehydrated boundary until we
3441+
// upgrade it. We return null instead of bailoutOnAlreadyFinishedWork.
3442+
return null;
3443+
}
3444+
}
3445+
3446+
// If this boundary is currently timed out, we need to decide
3447+
// whether to retry the primary children, or to skip over it and
3448+
// go straight to the fallback. Check the priority of the primary
3449+
// child fragment.
3450+
const primaryChildFragment: Fiber = (workInProgress.child: any);
3451+
const primaryChildLanes = primaryChildFragment.childLanes;
3452+
if (includesSomeLane(renderLanes, primaryChildLanes)) {
3453+
// The primary children have pending work. Use the normal path
3454+
// to attempt to render the primary children again.
3455+
return updateSuspenseComponent(current, workInProgress, renderLanes);
3456+
} else {
3457+
// The primary child fragment does not have pending work marked
3458+
// on it
3459+
pushSuspenseContext(
3460+
workInProgress,
3461+
setDefaultShallowSuspenseContext(suspenseStackCursor.current),
3462+
);
3463+
// The primary children do not have pending work with sufficient
3464+
// priority. Bailout.
3465+
const child = bailoutOnAlreadyFinishedWork(
3466+
current,
3467+
workInProgress,
3468+
renderLanes,
3469+
);
3470+
if (child !== null) {
3471+
// The fallback children have pending work. Skip over the
3472+
// primary children and work on the fallback.
3473+
return child.sibling;
3474+
} else {
3475+
// Note: We can return `null` here because we already checked
3476+
// whether there were nested context consumers, via the call to
3477+
// `bailoutOnAlreadyFinishedWork` above.
3478+
return null;
3479+
}
3480+
}
3481+
} else {
3482+
pushSuspenseContext(
3483+
workInProgress,
3484+
setDefaultShallowSuspenseContext(suspenseStackCursor.current),
3485+
);
3486+
}
3487+
break;
3488+
}
3489+
case SuspenseListComponent: {
3490+
const didSuspendBefore = (current.flags & DidCapture) !== NoFlags;
3491+
3492+
let hasChildWork = includesSomeLane(
3493+
renderLanes,
3494+
workInProgress.childLanes,
3495+
);
3496+
3497+
if (enableLazyContextPropagation && !hasChildWork) {
3498+
// Context changes may not have been propagated yet. We need to do
3499+
// that now, before we can decide whether to bail out.
3500+
// TODO: We use `childLanes` as a heuristic for whether there is
3501+
// remaining work in a few places, including
3502+
// `bailoutOnAlreadyFinishedWork` and
3503+
// `updateDehydratedSuspenseComponent`. We should maybe extract this
3504+
// into a dedicated function.
3505+
lazilyPropagateParentContextChanges(
3506+
current,
3507+
workInProgress,
3508+
renderLanes,
3509+
);
3510+
hasChildWork = includesSomeLane(renderLanes, workInProgress.childLanes);
3511+
}
3512+
3513+
if (didSuspendBefore) {
3514+
if (hasChildWork) {
3515+
// If something was in fallback state last time, and we have all the
3516+
// same children then we're still in progressive loading state.
3517+
// Something might get unblocked by state updates or retries in the
3518+
// tree which will affect the tail. So we need to use the normal
3519+
// path to compute the correct tail.
3520+
return updateSuspenseListComponent(
3521+
current,
3522+
workInProgress,
3523+
renderLanes,
3524+
);
3525+
}
3526+
// If none of the children had any work, that means that none of
3527+
// them got retried so they'll still be blocked in the same way
3528+
// as before. We can fast bail out.
3529+
workInProgress.flags |= DidCapture;
3530+
}
3531+
3532+
// If nothing suspended before and we're rendering the same children,
3533+
// then the tail doesn't matter. Anything new that suspends will work
3534+
// in the "together" mode, so we can continue from the state we had.
3535+
const renderState = workInProgress.memoizedState;
3536+
if (renderState !== null) {
3537+
// Reset to the "together" mode in case we've started a different
3538+
// update in the past but didn't complete it.
3539+
renderState.rendering = null;
3540+
renderState.tail = null;
3541+
renderState.lastEffect = null;
3542+
}
3543+
pushSuspenseContext(workInProgress, suspenseStackCursor.current);
3544+
3545+
if (hasChildWork) {
3546+
break;
3547+
} else {
3548+
// If none of the children had any work, that means that none of
3549+
// them got retried so they'll still be blocked in the same way
3550+
// as before. We can fast bail out.
3551+
return null;
3552+
}
3553+
}
3554+
case OffscreenComponent:
3555+
case LegacyHiddenComponent: {
3556+
// Need to check if the tree still needs to be deferred. This is
3557+
// almost identical to the logic used in the normal update path,
3558+
// so we'll just enter that. The only difference is we'll bail out
3559+
// at the next level instead of this one, because the child props
3560+
// have not changed. Which is fine.
3561+
// TODO: Probably should refactor `beginWork` to split the bailout
3562+
// path from the normal path. I'm tempted to do a labeled break here
3563+
// but I won't :)
3564+
workInProgress.lanes = NoLanes;
3565+
return updateOffscreenComponent(current, workInProgress, renderLanes);
3566+
}
3567+
case CacheComponent: {
3568+
if (enableCache) {
3569+
const cache: Cache = current.memoizedState.cache;
3570+
pushCacheProvider(workInProgress, cache);
3571+
}
3572+
break;
3573+
}
3574+
}
3575+
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
3576+
}
3577+
33503578
function beginWork(
33513579
current: Fiber | null,
33523580
workInProgress: Fiber,
33533581
renderLanes: Lanes,
33543582
): Fiber | null {
3355-
let updateLanes = workInProgress.lanes;
3356-
33573583
if (__DEV__) {
33583584
if (workInProgress._debugNeedsRemount && current !== null) {
33593585
// This will restart the begin phase with a new fiber.
@@ -3373,17 +3599,6 @@ function beginWork(
33733599
}
33743600

33753601
if (current !== null) {
3376-
// TODO: The factoring of this block is weird.
3377-
if (
3378-
enableLazyContextPropagation &&
3379-
!includesSomeLane(renderLanes, updateLanes)
3380-
) {
3381-
const dependencies = current.dependencies;
3382-
if (dependencies !== null && checkIfContextChanged(dependencies)) {
3383-
updateLanes = mergeLanes(updateLanes, renderLanes);
3384-
}
3385-
}
3386-
33873602
const oldProps = current.memoizedProps;
33883603
const newProps = workInProgress.pendingProps;
33893604

@@ -3396,221 +3611,27 @@ function beginWork(
33963611
// If props or context changed, mark the fiber as having performed work.
33973612
// This may be unset if the props are determined to be equal later (memo).
33983613
didReceiveUpdate = true;
3399-
} else if (!includesSomeLane(renderLanes, updateLanes)) {
3400-
didReceiveUpdate = false;
3401-
// This fiber does not have any pending work. Bailout without entering
3402-
// the begin phase. There's still some bookkeeping we that needs to be done
3403-
// in this optimized path, mostly pushing stuff onto the stack.
3404-
switch (workInProgress.tag) {
3405-
case HostRoot:
3406-
pushHostRootContext(workInProgress);
3407-
if (enableCache) {
3408-
const root: FiberRoot = workInProgress.stateNode;
3409-
const cache: Cache = current.memoizedState.cache;
3410-
pushCacheProvider(workInProgress, cache);
3411-
pushRootCachePool(root);
3412-
}
3413-
resetHydrationState();
3414-
break;
3415-
case HostComponent:
3416-
pushHostContext(workInProgress);
3417-
break;
3418-
case ClassComponent: {
3419-
const Component = workInProgress.type;
3420-
if (isLegacyContextProvider(Component)) {
3421-
pushLegacyContextProvider(workInProgress);
3422-
}
3423-
break;
3424-
}
3425-
case HostPortal:
3426-
pushHostContainer(
3427-
workInProgress,
3428-
workInProgress.stateNode.containerInfo,
3429-
);
3430-
break;
3431-
case ContextProvider: {
3432-
const newValue = workInProgress.memoizedProps.value;
3433-
const context: ReactContext<any> = workInProgress.type._context;
3434-
pushProvider(workInProgress, context, newValue);
3435-
break;
3436-
}
3437-
case Profiler:
3438-
if (enableProfilerTimer) {
3439-
// Profiler should only call onRender when one of its descendants actually rendered.
3440-
const hasChildWork = includesSomeLane(
3441-
renderLanes,
3442-
workInProgress.childLanes,
3443-
);
3444-
if (hasChildWork) {
3445-
workInProgress.flags |= Update;
3446-
}
3447-
3448-
if (enableProfilerCommitHooks) {
3449-
// Reset effect durations for the next eventual effect phase.
3450-
// These are reset during render to allow the DevTools commit hook a chance to read them,
3451-
const stateNode = workInProgress.stateNode;
3452-
stateNode.effectDuration = 0;
3453-
stateNode.passiveEffectDuration = 0;
3454-
}
3455-
}
3456-
break;
3457-
case SuspenseComponent: {
3458-
const state: SuspenseState | null = workInProgress.memoizedState;
3459-
if (state !== null) {
3460-
if (enableSuspenseServerRenderer) {
3461-
if (state.dehydrated !== null) {
3462-
pushSuspenseContext(
3463-
workInProgress,
3464-
setDefaultShallowSuspenseContext(suspenseStackCursor.current),
3465-
);
3466-
// We know that this component will suspend again because if it has
3467-
// been unsuspended it has committed as a resolved Suspense component.
3468-
// If it needs to be retried, it should have work scheduled on it.
3469-
workInProgress.flags |= DidCapture;
3470-
// We should never render the children of a dehydrated boundary until we
3471-
// upgrade it. We return null instead of bailoutOnAlreadyFinishedWork.
3472-
return null;
3473-
}
3474-
}
3475-
3476-
// If this boundary is currently timed out, we need to decide
3477-
// whether to retry the primary children, or to skip over it and
3478-
// go straight to the fallback. Check the priority of the primary
3479-
// child fragment.
3480-
const primaryChildFragment: Fiber = (workInProgress.child: any);
3481-
const primaryChildLanes = primaryChildFragment.childLanes;
3482-
if (includesSomeLane(renderLanes, primaryChildLanes)) {
3483-
// The primary children have pending work. Use the normal path
3484-
// to attempt to render the primary children again.
3485-
return updateSuspenseComponent(
3486-
current,
3487-
workInProgress,
3488-
renderLanes,
3489-
);
3490-
} else {
3491-
// The primary child fragment does not have pending work marked
3492-
// on it
3493-
pushSuspenseContext(
3494-
workInProgress,
3495-
setDefaultShallowSuspenseContext(suspenseStackCursor.current),
3496-
);
3497-
// The primary children do not have pending work with sufficient
3498-
// priority. Bailout.
3499-
const child = bailoutOnAlreadyFinishedWork(
3500-
current,
3501-
workInProgress,
3502-
renderLanes,
3503-
);
3504-
if (child !== null) {
3505-
// The fallback children have pending work. Skip over the
3506-
// primary children and work on the fallback.
3507-
return child.sibling;
3508-
} else {
3509-
// Note: We can return `null` here because we already checked
3510-
// whether there were nested context consumers, via the call to
3511-
// `bailoutOnAlreadyFinishedWork` above.
3512-
return null;
3513-
}
3514-
}
3515-
} else {
3516-
pushSuspenseContext(
3517-
workInProgress,
3518-
setDefaultShallowSuspenseContext(suspenseStackCursor.current),
3519-
);
3520-
}
3521-
break;
3522-
}
3523-
case SuspenseListComponent: {
3524-
const didSuspendBefore = (current.flags & DidCapture) !== NoFlags;
3525-
3526-
let hasChildWork = includesSomeLane(
3527-
renderLanes,
3528-
workInProgress.childLanes,
3529-
);
3530-
3531-
if (enableLazyContextPropagation && !hasChildWork) {
3532-
// Context changes may not have been propagated yet. We need to do
3533-
// that now, before we can decide whether to bail out.
3534-
// TODO: We use `childLanes` as a heuristic for whether there is
3535-
// remaining work in a few places, including
3536-
// `bailoutOnAlreadyFinishedWork` and
3537-
// `updateDehydratedSuspenseComponent`. We should maybe extract this
3538-
// into a dedicated function.
3539-
lazilyPropagateParentContextChanges(
3540-
current,
3541-
workInProgress,
3542-
renderLanes,
3543-
);
3544-
hasChildWork = includesSomeLane(
3545-
renderLanes,
3546-
workInProgress.childLanes,
3547-
);
3548-
}
3549-
3550-
if (didSuspendBefore) {
3551-
if (hasChildWork) {
3552-
// If something was in fallback state last time, and we have all the
3553-
// same children then we're still in progressive loading state.
3554-
// Something might get unblocked by state updates or retries in the
3555-
// tree which will affect the tail. So we need to use the normal
3556-
// path to compute the correct tail.
3557-
return updateSuspenseListComponent(
3558-
current,
3559-
workInProgress,
3560-
renderLanes,
3561-
);
3562-
}
3563-
// If none of the children had any work, that means that none of
3564-
// them got retried so they'll still be blocked in the same way
3565-
// as before. We can fast bail out.
3566-
workInProgress.flags |= DidCapture;
3567-
}
3568-
3569-
// If nothing suspended before and we're rendering the same children,
3570-
// then the tail doesn't matter. Anything new that suspends will work
3571-
// in the "together" mode, so we can continue from the state we had.
3572-
const renderState = workInProgress.memoizedState;
3573-
if (renderState !== null) {
3574-
// Reset to the "together" mode in case we've started a different
3575-
// update in the past but didn't complete it.
3576-
renderState.rendering = null;
3577-
renderState.tail = null;
3578-
renderState.lastEffect = null;
3579-
}
3580-
pushSuspenseContext(workInProgress, suspenseStackCursor.current);
3581-
3582-
if (hasChildWork) {
3583-
break;
3584-
} else {
3585-
// If none of the children had any work, that means that none of
3586-
// them got retried so they'll still be blocked in the same way
3587-
// as before. We can fast bail out.
3588-
return null;
3589-
}
3590-
}
3591-
case OffscreenComponent:
3592-
case LegacyHiddenComponent: {
3593-
// Need to check if the tree still needs to be deferred. This is
3594-
// almost identical to the logic used in the normal update path,
3595-
// so we'll just enter that. The only difference is we'll bail out
3596-
// at the next level instead of this one, because the child props
3597-
// have not changed. Which is fine.
3598-
// TODO: Probably should refactor `beginWork` to split the bailout
3599-
// path from the normal path. I'm tempted to do a labeled break here
3600-
// but I won't :)
3601-
workInProgress.lanes = NoLanes;
3602-
return updateOffscreenComponent(current, workInProgress, renderLanes);
3603-
}
3604-
case CacheComponent: {
3605-
if (enableCache) {
3606-
const cache: Cache = current.memoizedState.cache;
3607-
pushCacheProvider(workInProgress, cache);
3608-
}
3609-
break;
3610-
}
3611-
}
3612-
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
36133614
} else {
3615+
// Neither props nor legacy context changes. Check if there's a pending
3616+
// update or context change.
3617+
const hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(
3618+
current,
3619+
renderLanes,
3620+
);
3621+
if (
3622+
!hasScheduledUpdateOrContext &&
3623+
// If this is the second pass of an error or suspense boundary, there
3624+
// may not be work scheduled on `current`, so we check for this flag.
3625+
(workInProgress.flags & DidCapture) === NoFlags
3626+
) {
3627+
// No pending updates or context. Bail out now.
3628+
didReceiveUpdate = false;
3629+
return attemptEarlyBailoutIfNoScheduledUpdate(
3630+
current,
3631+
workInProgress,
3632+
renderLanes,
3633+
);
3634+
}
36143635
if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) {
36153636
// This is a special case that only exists for legacy mode.
36163637
// See https://github.com/facebook/react/pull/19216.
@@ -3649,7 +3670,6 @@ function beginWork(
36493670
current,
36503671
workInProgress,
36513672
elementType,
3652-
updateLanes,
36533673
renderLanes,
36543674
);
36553675
}
@@ -3742,7 +3762,6 @@ function beginWork(
37423762
workInProgress,
37433763
type,
37443764
resolvedProps,
3745-
updateLanes,
37463765
renderLanes,
37473766
);
37483767
}
@@ -3752,7 +3771,6 @@ function beginWork(
37523771
workInProgress,
37533772
workInProgress.type,
37543773
workInProgress.pendingProps,
3755-
updateLanes,
37563774
renderLanes,
37573775
);
37583776
}
@@ -3788,12 +3806,7 @@ function beginWork(
37883806
}
37893807
case CacheComponent: {
37903808
if (enableCache) {
3791-
return updateCacheComponent(
3792-
current,
3793-
workInProgress,
3794-
updateLanes,
3795-
renderLanes,
3796-
);
3809+
return updateCacheComponent(current, workInProgress, renderLanes);
37973810
}
37983811
break;
37993812
}

‎packages/react-reconciler/src/ReactFiberThrow.new.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,8 @@ function throwException(
383383
attachPingListener(root, wakeable, rootRenderLanes);
384384

385385
workInProgress.flags |= ShouldCapture;
386+
// TODO: I think we can remove this, since we now use `DidCapture` in
387+
// the begin phase to prevent an early bailout.
386388
workInProgress.lanes = rootRenderLanes;
387389

388390
return;

‎packages/react-reconciler/src/ReactFiberThrow.old.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,8 @@ function throwException(
383383
attachPingListener(root, wakeable, rootRenderLanes);
384384

385385
workInProgress.flags |= ShouldCapture;
386+
// TODO: I think we can remove this, since we now use `DidCapture` in
387+
// the begin phase to prevent an early bailout.
386388
workInProgress.lanes = rootRenderLanes;
387389

388390
return;

0 commit comments

Comments
 (0)
Please sign in to comment.