diff --git a/packages/react-reconciler/src/ReactFiberLane.new.js b/packages/react-reconciler/src/ReactFiberLane.new.js index 5bca8d747b0ce..794f599b2db5a 100644 --- a/packages/react-reconciler/src/ReactFiberLane.new.js +++ b/packages/react-reconciler/src/ReactFiberLane.new.js @@ -286,42 +286,34 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes { let nextLanes = NoLanes; let nextLanePriority = NoLanePriority; - const expiredLanes = root.expiredLanes; const suspendedLanes = root.suspendedLanes; const pingedLanes = root.pingedLanes; - // Check if any work has expired. - if (expiredLanes !== NoLanes) { - // TODO: Should entangle with SyncLane - nextLanes = expiredLanes; - nextLanePriority = return_highestLanePriority = SyncLanePriority; - } else { - // Do not work on any idle work until all the non-idle work has finished, - // even if the work is suspended. - const nonIdlePendingLanes = pendingLanes & NonIdleLanes; - if (nonIdlePendingLanes !== NoLanes) { - const nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes; - if (nonIdleUnblockedLanes !== NoLanes) { - nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes); + // Do not work on any idle work until all the non-idle work has finished, + // even if the work is suspended. + const nonIdlePendingLanes = pendingLanes & NonIdleLanes; + if (nonIdlePendingLanes !== NoLanes) { + const nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes; + if (nonIdleUnblockedLanes !== NoLanes) { + nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes); + nextLanePriority = return_highestLanePriority; + } else { + const nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes; + if (nonIdlePingedLanes !== NoLanes) { + nextLanes = getHighestPriorityLanes(nonIdlePingedLanes); nextLanePriority = return_highestLanePriority; - } else { - const nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes; - if (nonIdlePingedLanes !== NoLanes) { - nextLanes = getHighestPriorityLanes(nonIdlePingedLanes); - nextLanePriority = return_highestLanePriority; - } } + } + } else { + // The only remaining work is Idle. + const unblockedLanes = pendingLanes & ~suspendedLanes; + if (unblockedLanes !== NoLanes) { + nextLanes = getHighestPriorityLanes(unblockedLanes); + nextLanePriority = return_highestLanePriority; } else { - // The only remaining work is Idle. - const unblockedLanes = pendingLanes & ~suspendedLanes; - if (unblockedLanes !== NoLanes) { - nextLanes = getHighestPriorityLanes(unblockedLanes); + if (pingedLanes !== NoLanes) { + nextLanes = getHighestPriorityLanes(pingedLanes); nextLanePriority = return_highestLanePriority; - } else { - if (pingedLanes !== NoLanes) { - nextLanes = getHighestPriorityLanes(pingedLanes); - nextLanePriority = return_highestLanePriority; - } } } } @@ -463,6 +455,7 @@ export function markStarvedLanesAsExpired( // expiration time. If so, we'll assume the update is being starved and mark // it as expired to force it to finish. let lanes = pendingLanes; + let expiredLanes = 0; while (lanes > 0) { const index = pickArbitraryLaneIndex(lanes); const lane = 1 << index; @@ -481,11 +474,15 @@ export function markStarvedLanesAsExpired( } } else if (expirationTime <= currentTime) { // This lane expired - root.expiredLanes |= lane; + expiredLanes |= lane; } lanes &= ~lane; } + + if (expiredLanes !== 0) { + markRootExpired(root, expiredLanes); + } } // This returns the highest priority pending lanes regardless of whether they @@ -668,7 +665,17 @@ export function markRootPinged( } export function markRootExpired(root: FiberRoot, expiredLanes: Lanes) { - root.expiredLanes |= expiredLanes & root.pendingLanes; + const entanglements = root.entanglements; + const SyncLaneIndex = 0; + entanglements[SyncLaneIndex] |= expiredLanes; + root.entangledLanes |= SyncLane; + root.pendingLanes |= SyncLane; +} + +export function areLanesExpired(root: FiberRoot, lanes: Lanes) { + const SyncLaneIndex = 0; + const entanglements = root.entanglements; + return (entanglements[SyncLaneIndex] & lanes) !== NoLanes; } export function markRootMutableRead(root: FiberRoot, updateLane: Lane) { @@ -684,7 +691,6 @@ export function markRootFinished(root: FiberRoot, remainingLanes: Lanes) { root.suspendedLanes = 0; root.pingedLanes = 0; - root.expiredLanes &= remainingLanes; root.mutableReadLanes &= remainingLanes; root.entangledLanes &= remainingLanes; diff --git a/packages/react-reconciler/src/ReactFiberLane.old.js b/packages/react-reconciler/src/ReactFiberLane.old.js index 6b96120348565..0bb480d274a6c 100644 --- a/packages/react-reconciler/src/ReactFiberLane.old.js +++ b/packages/react-reconciler/src/ReactFiberLane.old.js @@ -286,42 +286,34 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes { let nextLanes = NoLanes; let nextLanePriority = NoLanePriority; - const expiredLanes = root.expiredLanes; const suspendedLanes = root.suspendedLanes; const pingedLanes = root.pingedLanes; - // Check if any work has expired. - if (expiredLanes !== NoLanes) { - // TODO: Should entangle with SyncLane - nextLanes = expiredLanes; - nextLanePriority = return_highestLanePriority = SyncLanePriority; - } else { - // Do not work on any idle work until all the non-idle work has finished, - // even if the work is suspended. - const nonIdlePendingLanes = pendingLanes & NonIdleLanes; - if (nonIdlePendingLanes !== NoLanes) { - const nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes; - if (nonIdleUnblockedLanes !== NoLanes) { - nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes); + // Do not work on any idle work until all the non-idle work has finished, + // even if the work is suspended. + const nonIdlePendingLanes = pendingLanes & NonIdleLanes; + if (nonIdlePendingLanes !== NoLanes) { + const nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes; + if (nonIdleUnblockedLanes !== NoLanes) { + nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes); + nextLanePriority = return_highestLanePriority; + } else { + const nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes; + if (nonIdlePingedLanes !== NoLanes) { + nextLanes = getHighestPriorityLanes(nonIdlePingedLanes); nextLanePriority = return_highestLanePriority; - } else { - const nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes; - if (nonIdlePingedLanes !== NoLanes) { - nextLanes = getHighestPriorityLanes(nonIdlePingedLanes); - nextLanePriority = return_highestLanePriority; - } } + } + } else { + // The only remaining work is Idle. + const unblockedLanes = pendingLanes & ~suspendedLanes; + if (unblockedLanes !== NoLanes) { + nextLanes = getHighestPriorityLanes(unblockedLanes); + nextLanePriority = return_highestLanePriority; } else { - // The only remaining work is Idle. - const unblockedLanes = pendingLanes & ~suspendedLanes; - if (unblockedLanes !== NoLanes) { - nextLanes = getHighestPriorityLanes(unblockedLanes); + if (pingedLanes !== NoLanes) { + nextLanes = getHighestPriorityLanes(pingedLanes); nextLanePriority = return_highestLanePriority; - } else { - if (pingedLanes !== NoLanes) { - nextLanes = getHighestPriorityLanes(pingedLanes); - nextLanePriority = return_highestLanePriority; - } } } } @@ -463,6 +455,7 @@ export function markStarvedLanesAsExpired( // expiration time. If so, we'll assume the update is being starved and mark // it as expired to force it to finish. let lanes = pendingLanes; + let expiredLanes = 0; while (lanes > 0) { const index = pickArbitraryLaneIndex(lanes); const lane = 1 << index; @@ -481,11 +474,15 @@ export function markStarvedLanesAsExpired( } } else if (expirationTime <= currentTime) { // This lane expired - root.expiredLanes |= lane; + expiredLanes |= lane; } lanes &= ~lane; } + + if (expiredLanes !== 0) { + markRootExpired(root, expiredLanes); + } } // This returns the highest priority pending lanes regardless of whether they @@ -668,7 +665,17 @@ export function markRootPinged( } export function markRootExpired(root: FiberRoot, expiredLanes: Lanes) { - root.expiredLanes |= expiredLanes & root.pendingLanes; + const entanglements = root.entanglements; + const SyncLaneIndex = 0; + entanglements[SyncLaneIndex] |= expiredLanes; + root.entangledLanes |= SyncLane; + root.pendingLanes |= SyncLane; +} + +export function areLanesExpired(root: FiberRoot, lanes: Lanes) { + const SyncLaneIndex = 0; + const entanglements = root.entanglements; + return (entanglements[SyncLaneIndex] & lanes) !== NoLanes; } export function markRootMutableRead(root: FiberRoot, updateLane: Lane) { @@ -684,7 +691,6 @@ export function markRootFinished(root: FiberRoot, remainingLanes: Lanes) { root.suspendedLanes = 0; root.pingedLanes = 0; - root.expiredLanes &= remainingLanes; root.mutableReadLanes &= remainingLanes; root.entangledLanes &= remainingLanes; diff --git a/packages/react-reconciler/src/ReactFiberRoot.new.js b/packages/react-reconciler/src/ReactFiberRoot.new.js index 14c20e1f7edee..dfc859a465218 100644 --- a/packages/react-reconciler/src/ReactFiberRoot.new.js +++ b/packages/react-reconciler/src/ReactFiberRoot.new.js @@ -48,7 +48,6 @@ function FiberRootNode(containerInfo, tag, hydrate) { this.pendingLanes = NoLanes; this.suspendedLanes = NoLanes; this.pingedLanes = NoLanes; - this.expiredLanes = NoLanes; this.mutableReadLanes = NoLanes; this.finishedLanes = NoLanes; diff --git a/packages/react-reconciler/src/ReactFiberRoot.old.js b/packages/react-reconciler/src/ReactFiberRoot.old.js index 3bad9a026be0b..138fb9c38a1a6 100644 --- a/packages/react-reconciler/src/ReactFiberRoot.old.js +++ b/packages/react-reconciler/src/ReactFiberRoot.old.js @@ -48,7 +48,6 @@ function FiberRootNode(containerInfo, tag, hydrate) { this.pendingLanes = NoLanes; this.suspendedLanes = NoLanes; this.pingedLanes = NoLanes; - this.expiredLanes = NoLanes; this.mutableReadLanes = NoLanes; this.finishedLanes = NoLanes; diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index e86cb6777c286..edfc46cece75a 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -157,6 +157,7 @@ import { markRootExpired, markRootFinished, lanePriorityToSchedulerPriority, + areLanesExpired, } from './ReactFiberLane.new'; import { DiscreteEventPriority, @@ -966,7 +967,7 @@ function performSyncWorkOnRoot(root) { let exitStatus; if ( root === workInProgressRoot && - includesSomeLane(root.expiredLanes, workInProgressRootRenderLanes) + areLanesExpired(root, workInProgressRootRenderLanes) ) { // There's a partial tree, and at least one of its lanes has expired. Finish // rendering it before rendering the rest of the expired work. @@ -1022,12 +1023,16 @@ function performSyncWorkOnRoot(root) { return null; } +// TODO: Do we still need this API? I think we can delete it. Was only used +// internally. export function flushRoot(root: FiberRoot, lanes: Lanes) { - markRootExpired(root, lanes); - ensureRootIsScheduled(root, now()); - if ((executionContext & (RenderContext | CommitContext)) === NoContext) { - resetRenderTimer(); - flushSyncCallbackQueue(); + if (lanes !== NoLanes) { + markRootExpired(root, lanes); + ensureRootIsScheduled(root, now()); + if ((executionContext & (RenderContext | CommitContext)) === NoContext) { + resetRenderTimer(); + flushSyncCallbackQueue(); + } } } diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index 7d452fe2699f8..542567182c4c9 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -157,6 +157,7 @@ import { markRootExpired, markRootFinished, lanePriorityToSchedulerPriority, + areLanesExpired, } from './ReactFiberLane.old'; import { DiscreteEventPriority, @@ -966,7 +967,7 @@ function performSyncWorkOnRoot(root) { let exitStatus; if ( root === workInProgressRoot && - includesSomeLane(root.expiredLanes, workInProgressRootRenderLanes) + areLanesExpired(root, workInProgressRootRenderLanes) ) { // There's a partial tree, and at least one of its lanes has expired. Finish // rendering it before rendering the rest of the expired work. @@ -1022,12 +1023,16 @@ function performSyncWorkOnRoot(root) { return null; } +// TODO: Do we still need this API? I think we can delete it. Was only used +// internally. export function flushRoot(root: FiberRoot, lanes: Lanes) { - markRootExpired(root, lanes); - ensureRootIsScheduled(root, now()); - if ((executionContext & (RenderContext | CommitContext)) === NoContext) { - resetRenderTimer(); - flushSyncCallbackQueue(); + if (lanes !== NoLanes) { + markRootExpired(root, lanes); + ensureRootIsScheduled(root, now()); + if ((executionContext & (RenderContext | CommitContext)) === NoContext) { + resetRenderTimer(); + flushSyncCallbackQueue(); + } } } diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index e5fd43e6f416e..4e5a415f165f6 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -230,7 +230,6 @@ type BaseFiberRootProperties = {| pendingLanes: Lanes, suspendedLanes: Lanes, pingedLanes: Lanes, - expiredLanes: Lanes, mutableReadLanes: Lanes, finishedLanes: Lanes, diff --git a/packages/react-reconciler/src/__tests__/ReactExpiration-test.js b/packages/react-reconciler/src/__tests__/ReactExpiration-test.js index cf8b539433e46..5fc98d980e299 100644 --- a/packages/react-reconciler/src/__tests__/ReactExpiration-test.js +++ b/packages/react-reconciler/src/__tests__/ReactExpiration-test.js @@ -682,28 +682,14 @@ describe('ReactExpiration', () => { updateHighPri(); // Both normal pri updates should have expired. - if (gate(flags => flags.FIXME)) { - // The sync update and the expired normal pri updates render in a - // single batch. - expect(Scheduler).toHaveYielded([ - 'Sibling', - 'High pri: 1', - 'Normal pri: 2', - 'Sibling', - ]); - } else { - expect(Scheduler).toHaveYielded([ - 'Sibling', - 'High pri: 0', - 'Normal pri: 2', - 'Sibling', - // TODO: This is the sync update. We should have rendered it in the same - // batch as the expired update. - 'High pri: 1', - 'Normal pri: 2', - 'Sibling', - ]); - } + // The sync update and the expired normal pri updates render in a + // single batch. + expect(Scheduler).toHaveYielded([ + 'Sibling', + 'High pri: 1', + 'Normal pri: 2', + 'Sibling', + ]); }); });