Skip to content

Commit c650bf1

Browse files
committed
Throttle retries even if everything has loaded (#26611)
If a Suspense fallback is shown, and the data finishes loading really quickly after that, we throttle the content from appearing for 500ms to reduce thrash. This already works for successive fallback states (like if one fallback is nested inside another) but it wasn't being applied to the final step in the sequence: if there were no more unresolved Suspense boundaries in the tree, the content would appear immediately. This fixes the throttling behavior so that it applies to all renders that are the result of suspended data being loaded. (Our internal jargon term for this is a "retry".) DiffTrain build for commit 8256781.
1 parent 66c7f48 commit c650bf1

File tree

13 files changed

+469
-685
lines changed

13 files changed

+469
-685
lines changed

compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react-test-renderer/cjs/ReactTestRenderer-dev.js

Lines changed: 65 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -20260,109 +20260,89 @@ function queueRecoverableErrors(errors) {
2026020260
}
2026120261

2026220262
function finishConcurrentRender(root, exitStatus, finishedWork, lanes) {
20263+
// TODO: The fact that most of these branches are identical suggests that some
20264+
// of the exit statuses are not best modeled as exit statuses and should be
20265+
// tracked orthogonally.
2026320266
switch (exitStatus) {
2026420267
case RootInProgress:
2026520268
case RootFatalErrored: {
2026620269
throw new Error("Root did not complete. This is a bug in React.");
2026720270
}
2026820271

20269-
case RootErrored: {
20270-
// We should have already attempted to retry this tree. If we reached
20271-
// this point, it errored again. Commit it.
20272-
commitRootWhenReady(
20273-
root,
20274-
finishedWork,
20275-
workInProgressRootRecoverableErrors,
20276-
workInProgressTransitions,
20277-
lanes
20278-
);
20279-
break;
20280-
}
20281-
20282-
case RootSuspended: {
20283-
markRootSuspended(root, lanes); // We have an acceptable loading state. We need to figure out if we
20284-
// should immediately commit it or wait a bit.
20285-
20286-
if (
20287-
includesOnlyRetries(lanes) && // do not delay if we're inside an act() scope
20288-
!shouldForceFlushFallbacksInDEV()
20289-
) {
20290-
// This render only included retries, no updates. Throttle committing
20291-
// retries so that we don't show too many loading states too quickly.
20292-
var msUntilTimeout =
20293-
globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now$1(); // Don't bother with a very short suspense time.
20294-
20295-
if (msUntilTimeout > 10) {
20296-
var nextLanes = getNextLanes(root, NoLanes);
20297-
20298-
if (nextLanes !== NoLanes) {
20299-
// There's additional work on this root.
20300-
break;
20301-
} // The render is suspended, it hasn't timed out, and there's no
20302-
// lower priority work to do. Instead of committing the fallback
20303-
// immediately, wait for more data to arrive.
20304-
20305-
root.timeoutHandle = scheduleTimeout(
20306-
commitRootWhenReady.bind(
20307-
null,
20308-
root,
20309-
finishedWork,
20310-
workInProgressRootRecoverableErrors,
20311-
workInProgressTransitions,
20312-
lanes
20313-
),
20314-
msUntilTimeout
20315-
);
20316-
break;
20317-
}
20318-
} // The work expired. Commit immediately.
20319-
20320-
commitRootWhenReady(
20321-
root,
20322-
finishedWork,
20323-
workInProgressRootRecoverableErrors,
20324-
workInProgressTransitions,
20325-
lanes
20326-
);
20327-
break;
20328-
}
20329-
2033020272
case RootSuspendedWithDelay: {
20331-
markRootSuspended(root, lanes);
20332-
2033320273
if (includesOnlyTransitions(lanes)) {
2033420274
// This is a transition, so we should exit without committing a
2033520275
// placeholder and without scheduling a timeout. Delay indefinitely
2033620276
// until we receive more data.
20337-
break;
20277+
markRootSuspended(root, lanes);
20278+
return;
2033820279
} // Commit the placeholder.
2033920280

20340-
commitRootWhenReady(
20341-
root,
20342-
finishedWork,
20343-
workInProgressRootRecoverableErrors,
20344-
workInProgressTransitions,
20345-
lanes
20346-
);
2034720281
break;
2034820282
}
2034920283

20284+
case RootErrored:
20285+
case RootSuspended:
2035020286
case RootCompleted: {
20351-
// The work completed.
20352-
commitRootWhenReady(
20353-
root,
20354-
finishedWork,
20355-
workInProgressRootRecoverableErrors,
20356-
workInProgressTransitions,
20357-
lanes
20358-
);
2035920287
break;
2036020288
}
2036120289

2036220290
default: {
2036320291
throw new Error("Unknown root exit status.");
2036420292
}
2036520293
}
20294+
20295+
if (shouldForceFlushFallbacksInDEV()) {
20296+
// We're inside an `act` scope. Commit immediately.
20297+
commitRoot(
20298+
root,
20299+
workInProgressRootRecoverableErrors,
20300+
workInProgressTransitions
20301+
);
20302+
} else {
20303+
if (includesOnlyRetries(lanes)) {
20304+
// This render only included retries, no updates. Throttle committing
20305+
// retries so that we don't show too many loading states too quickly.
20306+
var msUntilTimeout =
20307+
globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now$1(); // Don't bother with a very short suspense time.
20308+
20309+
if (msUntilTimeout > 10) {
20310+
markRootSuspended(root, lanes);
20311+
var nextLanes = getNextLanes(root, NoLanes);
20312+
20313+
if (nextLanes !== NoLanes) {
20314+
// There's additional work we can do on this root. We might as well
20315+
// attempt to work on that while we're suspended.
20316+
return;
20317+
} // The render is suspended, it hasn't timed out, and there's no
20318+
// lower priority work to do. Instead of committing the fallback
20319+
// immediately, wait for more data to arrive.
20320+
// TODO: Combine retry throttling with Suspensey commits. Right now they
20321+
// run one after the other.
20322+
20323+
root.timeoutHandle = scheduleTimeout(
20324+
commitRootWhenReady.bind(
20325+
null,
20326+
root,
20327+
finishedWork,
20328+
workInProgressRootRecoverableErrors,
20329+
workInProgressTransitions,
20330+
lanes
20331+
),
20332+
msUntilTimeout
20333+
);
20334+
return;
20335+
}
20336+
}
20337+
20338+
commitRootWhenReady(
20339+
root,
20340+
finishedWork,
20341+
workInProgressRootRecoverableErrors,
20342+
workInProgressTransitions,
20343+
lanes
20344+
);
20345+
}
2036620346
}
2036720347

2036820348
function commitRootWhenReady(
@@ -20372,6 +20352,8 @@ function commitRootWhenReady(
2037220352
transitions,
2037320353
lanes
2037420354
) {
20355+
// TODO: Combine retry throttling with Suspensey commits. Right now they run
20356+
// one after the other.
2037520357
if (includesOnlyNonUrgentLanes(lanes)) {
2037620358
// the suspensey resources. The renderer is responsible for accumulating
2037720359
// all the load events. This all happens in a single synchronous
@@ -20391,22 +20373,14 @@ function commitRootWhenReady(
2039120373
// us that it's ready. This will be canceled if we start work on the
2039220374
// root again.
2039320375
root.cancelPendingCommit = schedulePendingCommit(
20394-
commitRoot.bind(
20395-
null,
20396-
root,
20397-
workInProgressRootRecoverableErrors,
20398-
workInProgressTransitions
20399-
)
20376+
commitRoot.bind(null, root, recoverableErrors, transitions)
2040020377
);
20378+
markRootSuspended(root, lanes);
2040120379
return;
2040220380
}
2040320381
} // Otherwise, commit immediately.
2040420382

20405-
commitRoot(
20406-
root,
20407-
workInProgressRootRecoverableErrors,
20408-
workInProgressTransitions
20409-
);
20383+
commitRoot(root, recoverableErrors, transitions);
2041020384
}
2041120385

2041220386
function isRenderConsistentWithExternalStores(finishedWork) {
@@ -23752,7 +23726,7 @@ function createFiberRoot(
2375223726
return root;
2375323727
}
2375423728

23755-
var ReactVersion = "18.3.0-next-72c890e31-20230412";
23729+
var ReactVersion = "18.3.0-next-8256781fd-20230412";
2375623730

2375723731
// Might add PROFILE later.
2375823732

compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react-test-renderer/cjs/ReactTestRenderer-prod.js

Lines changed: 45 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -6498,70 +6498,51 @@ function performConcurrentWorkOnRoot(root, didTimeout) {
64986498
}
64996499
root.finishedWork = originallyAttemptedLanes;
65006500
root.finishedLanes = lanes;
6501-
switch (didTimeout) {
6502-
case 0:
6503-
case 1:
6504-
throw Error("Root did not complete. This is a bug in React.");
6505-
case 2:
6506-
commitRootWhenReady(
6507-
root,
6508-
originallyAttemptedLanes,
6509-
workInProgressRootRecoverableErrors,
6510-
workInProgressTransitions,
6511-
lanes
6512-
);
6513-
break;
6514-
case 3:
6515-
markRootSuspended(root, lanes);
6516-
if (
6517-
(lanes & 125829120) === lanes &&
6518-
((didTimeout = globalMostRecentFallbackTime + 500 - now()),
6519-
10 < didTimeout)
6520-
) {
6521-
if (0 !== getNextLanes(root, 0)) break;
6522-
root.timeoutHandle = scheduleTimeout(
6523-
commitRootWhenReady.bind(
6524-
null,
6525-
root,
6526-
originallyAttemptedLanes,
6527-
workInProgressRootRecoverableErrors,
6528-
workInProgressTransitions,
6529-
lanes
6530-
),
6531-
didTimeout
6532-
);
6501+
a: {
6502+
switch (didTimeout) {
6503+
case 0:
6504+
case 1:
6505+
throw Error("Root did not complete. This is a bug in React.");
6506+
case 4:
6507+
if ((lanes & 8388480) === lanes) {
6508+
markRootSuspended(root, lanes);
6509+
break a;
6510+
}
65336511
break;
6534-
}
6535-
commitRootWhenReady(
6536-
root,
6537-
originallyAttemptedLanes,
6538-
workInProgressRootRecoverableErrors,
6539-
workInProgressTransitions,
6540-
lanes
6541-
);
6542-
break;
6543-
case 4:
6512+
case 2:
6513+
case 3:
6514+
case 5:
6515+
break;
6516+
default:
6517+
throw Error("Unknown root exit status.");
6518+
}
6519+
if (
6520+
(lanes & 125829120) === lanes &&
6521+
((didTimeout = globalMostRecentFallbackTime + 500 - now()),
6522+
10 < didTimeout)
6523+
) {
65446524
markRootSuspended(root, lanes);
6545-
if ((lanes & 8388480) === lanes) break;
6546-
commitRootWhenReady(
6547-
root,
6548-
originallyAttemptedLanes,
6549-
workInProgressRootRecoverableErrors,
6550-
workInProgressTransitions,
6551-
lanes
6552-
);
6553-
break;
6554-
case 5:
6555-
commitRootWhenReady(
6556-
root,
6557-
originallyAttemptedLanes,
6558-
workInProgressRootRecoverableErrors,
6559-
workInProgressTransitions,
6560-
lanes
6525+
if (0 !== getNextLanes(root, 0)) break a;
6526+
root.timeoutHandle = scheduleTimeout(
6527+
commitRootWhenReady.bind(
6528+
null,
6529+
root,
6530+
originallyAttemptedLanes,
6531+
workInProgressRootRecoverableErrors,
6532+
workInProgressTransitions,
6533+
lanes
6534+
),
6535+
didTimeout
65616536
);
6562-
break;
6563-
default:
6564-
throw Error("Unknown root exit status.");
6537+
break a;
6538+
}
6539+
commitRootWhenReady(
6540+
root,
6541+
originallyAttemptedLanes,
6542+
workInProgressRootRecoverableErrors,
6543+
workInProgressTransitions,
6544+
lanes
6545+
);
65656546
}
65666547
}
65676548
}
@@ -6612,11 +6593,7 @@ function commitRootWhenReady(
66126593
lanes
66136594
) {
66146595
0 === (lanes & 42) && accumulateSuspenseyCommitOnFiber(finishedWork);
6615-
commitRoot(
6616-
root,
6617-
workInProgressRootRecoverableErrors,
6618-
workInProgressTransitions
6619-
);
6596+
commitRoot(root, recoverableErrors, transitions);
66206597
}
66216598
function isRenderConsistentWithExternalStores(finishedWork) {
66226599
for (var node = finishedWork; ; ) {
@@ -8610,7 +8587,7 @@ var devToolsConfig$jscomp$inline_1021 = {
86108587
throw Error("TestRenderer does not support findFiberByHostInstance()");
86118588
},
86128589
bundleType: 0,
8613-
version: "18.3.0-next-72c890e31-20230412",
8590+
version: "18.3.0-next-8256781fd-20230412",
86148591
rendererPackageName: "react-test-renderer"
86158592
};
86168593
var internals$jscomp$inline_1204 = {
@@ -8641,7 +8618,7 @@ var internals$jscomp$inline_1204 = {
86418618
scheduleRoot: null,
86428619
setRefreshHandler: null,
86438620
getCurrentFiber: null,
8644-
reconcilerVersion: "18.3.0-next-72c890e31-20230412"
8621+
reconcilerVersion: "18.3.0-next-8256781fd-20230412"
86458622
};
86468623
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
86478624
var hook$jscomp$inline_1205 = __REACT_DEVTOOLS_GLOBAL_HOOK__;

0 commit comments

Comments
 (0)