Use lanes to check if a render is a Suspense retry (#19307)
Now that Suspense retries have their own dedicated set of lanes (#19287), we can determine if a render includes only retries by checking if its lanes are a subset of the retry lanes. Previously we inferred this by checking `workInProgressRootLatestProcessedEventTime`. If it's not set, that implies that no updates were processed in the current render, which implies it must be a Suspense retry. The eventual plan is to get rid of `workInProgressRootLatestProcessedEventTime` and instead track event times on the root; this change is one the steps toward that goal. The relevant tests were originally added in #15769.
Andrew Clark committed
Jul 10, 2020 at 08:58 UTC
970fa122d8188bafa600e9b5214833487fbf1092
3 files changed
+18
-31
packages/react-reconciler/src/ReactFiberLane.js
+4
-1
@@ -451,9 +451,12 @@ export function getLanesToRetrySynchronouslyOnError(root: FiberRoot): Lanes {
451
export function returnNextLanesPriority() {
452
return return_highestLanePriority;
453
}
454
-export function hasUpdatePriority(lanes: Lanes) {
454
+export function includesNonIdleWork(lanes: Lanes) {
455
return (lanes & NonIdleLanes) !== NoLanes;
456
}
457
+export function includesOnlyRetries(lanes: Lanes) {
458
+ return (lanes & RetryLanes) === lanes;
459
+}
460
461
// To ensure consistency across multiple updates in the same event, this should
462
// be a pure function, so that it always returns the same lane for given inputs.
packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+7
-15
@@ -132,7 +132,8 @@ import {
132
removeLanes,
133
pickArbitraryLane,
134
hasDiscreteLanes,
135
- hasUpdatePriority,
135
+ includesNonIdleWork,
136
+ includesOnlyRetries,
137
getNextLanes,
138
returnNextLanesPriority,
139
setCurrentUpdateLanePriority,
@@ -847,22 +848,13 @@ function finishConcurrentRender(root, finishedWork, exitStatus, lanes) {
848
// We have an acceptable loading state. We need to figure out if we
849
// should immediately commit it or wait a bit.
850
850
- // If we have processed new updates during this render, we may now
851
- // have a new loading state ready. We want to ensure that we commit
852
- // that as soon as possible.
853
- const hasNotProcessedNewUpdates =
854
- workInProgressRootLatestProcessedEventTime === NoTimestamp;
851
if (
856
- hasNotProcessedNewUpdates &&
852
+ includesOnlyRetries(lanes) &&
853
// do not delay if we're inside an act() scope
854
!shouldForceFlushFallbacksInDEV()
855
) {
860
- // If we have not processed any new updates during this pass, then
861
- // this is either a retry of an existing fallback state or a
862
- // hidden tree. Hidden trees shouldn't be batched with other work
863
- // and after that's fixed it can only be a retry. We're going to
864
- // throttle committing retries so that we don't show too many
865
- // loading states too quickly.
856
+ // This render only included retries, no updates. Throttle committing
857
+ // retries so that we don't show too many loading states too quickly.
858
const msUntilTimeout =
859
globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now();
860
// Don't bother with a very short suspense time.
@@ -1475,8 +1467,8 @@ export function renderDidSuspendDelayIfPossible(): void {
1467
// this render.
1468
if (
1469
workInProgressRoot !== null &&
1478
- (hasUpdatePriority(workInProgressRootSkippedLanes) ||
1479
- hasUpdatePriority(workInProgressRootUpdatedLanes))
1470
+ (includesNonIdleWork(workInProgressRootSkippedLanes) ||
1471
+ includesNonIdleWork(workInProgressRootUpdatedLanes))
1472
) {
1473
// Mark the current render as suspended so that we switch to working on
1474
// the updates that were skipped. Usually we only suspend at the end of
packages/react-reconciler/src/ReactFiberWorkLoop.old.js
+7
-15
@@ -155,7 +155,8 @@ import {
155
removeLanes,
156
pickArbitraryLane,
157
hasDiscreteLanes,
158
- hasUpdatePriority,
158
+ includesNonIdleWork,
159
+ includesOnlyRetries,
160
getNextLanes,
161
returnNextLanesPriority,
162
setCurrentUpdateLanePriority,
@@ -870,22 +871,13 @@ function finishConcurrentRender(root, finishedWork, exitStatus, lanes) {
871
// We have an acceptable loading state. We need to figure out if we
872
// should immediately commit it or wait a bit.
873
873
- // If we have processed new updates during this render, we may now
874
- // have a new loading state ready. We want to ensure that we commit
875
- // that as soon as possible.
876
- const hasNotProcessedNewUpdates =
877
- workInProgressRootLatestProcessedEventTime === NoTimestamp;
874
if (
879
- hasNotProcessedNewUpdates &&
875
+ includesOnlyRetries(lanes) &&
876
// do not delay if we're inside an act() scope
877
!shouldForceFlushFallbacksInDEV()
878
) {
883
- // If we have not processed any new updates during this pass, then
884
- // this is either a retry of an existing fallback state or a
885
- // hidden tree. Hidden trees shouldn't be batched with other work
886
- // and after that's fixed it can only be a retry. We're going to
887
- // throttle committing retries so that we don't show too many
888
- // loading states too quickly.
879
+ // This render only included retries, no updates. Throttle committing
880
+ // retries so that we don't show too many loading states too quickly.
881
const msUntilTimeout =
882
globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now();
883
// Don't bother with a very short suspense time.
@@ -1498,8 +1490,8 @@ export function renderDidSuspendDelayIfPossible(): void {
1490
// this render.
1491
if (
1492
workInProgressRoot !== null &&
1501
- (hasUpdatePriority(workInProgressRootSkippedLanes) ||
1502
- hasUpdatePriority(workInProgressRootUpdatedLanes))
1493
+ (includesNonIdleWork(workInProgressRootSkippedLanes) ||
1494
+ includesNonIdleWork(workInProgressRootUpdatedLanes))
1495
) {
1496
// Mark the current render as suspended so that we switch to working on
1497
// the updates that were skipped. Usually we only suspend at the end of