Revert "Make prerendering always non-blocking" (#31080)
Reverts facebook/react#31056
Ricky committed
Sep 26, 2024 at 16:48 UTC
76aee6f39d94caa04c11be92d75d12cb9ee56494
7 files changed
+137
-298
packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js
+1
-1
@@ -744,7 +744,7 @@ describe('ReactDOMFiberAsync', () => {
744
// Because it suspended, it remains on the current path
745
expect(div.textContent).toBe('/path/a');
746
});
747
- assertLog(gate('enableSiblingPrerendering') ? ['Suspend! [/path/b]'] : []);
747
+ assertLog([]);
748
749
await act(async () => {
750
resolvePromise();
packages/react-reconciler/src/ReactFiberCompleteWork.js
+1
-4
@@ -42,7 +42,6 @@ import {
42
enableRenderableContext,
43
passChildrenWhenCloningPersistedNodes,
44
disableLegacyMode,
45
- enableSiblingPrerendering,
45
} from 'shared/ReactFeatureFlags';
46
47
import {now} from './Scheduler';
@@ -623,9 +622,7 @@ function scheduleRetryEffect(
622
623
// Track the lanes that have been scheduled for an immediate retry so that
624
// we can mark them as suspended upon committing the root.
626
- if (enableSiblingPrerendering) {
627
- markSpawnedRetryLane(retryLane);
628
- }
625
+ markSpawnedRetryLane(retryLane);
626
}
627
}
628
packages/react-reconciler/src/ReactFiberLane.js
+12
-20
@@ -27,7 +27,6 @@ import {
27
transitionLaneExpirationMs,
28
retryLaneExpirationMs,
29
disableLegacyMode,
30
- enableSiblingPrerendering,
30
} from 'shared/ReactFeatureFlags';
31
import {isDevToolsPresent} from './ReactFiberDevToolsHook';
32
import {clz32} from './clz32';
@@ -271,13 +270,11 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes {
270
if (nonIdlePingedLanes !== NoLanes) {
271
nextLanes = getHighestPriorityLanes(nonIdlePingedLanes);
272
} else {
274
- if (enableSiblingPrerendering) {
275
- // Nothing has been pinged. Check for lanes that need to be prewarmed.
276
- if (!rootHasPendingCommit) {
277
- const lanesToPrewarm = nonIdlePendingLanes & ~warmLanes;
278
- if (lanesToPrewarm !== NoLanes) {
279
- nextLanes = getHighestPriorityLanes(lanesToPrewarm);
280
- }
273
+ // Nothing has been pinged. Check for lanes that need to be prewarmed.
274
+ if (!rootHasPendingCommit) {
275
+ const lanesToPrewarm = nonIdlePendingLanes & ~warmLanes;
276
+ if (lanesToPrewarm !== NoLanes) {
277
+ nextLanes = getHighestPriorityLanes(lanesToPrewarm);
278
}
279
}
280
}
@@ -297,13 +294,11 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes {
294
if (pingedLanes !== NoLanes) {
295
nextLanes = getHighestPriorityLanes(pingedLanes);
296
} else {
300
- if (enableSiblingPrerendering) {
301
- // Nothing has been pinged. Check for lanes that need to be prewarmed.
302
- if (!rootHasPendingCommit) {
303
- const lanesToPrewarm = pendingLanes & ~warmLanes;
304
- if (lanesToPrewarm !== NoLanes) {
305
- nextLanes = getHighestPriorityLanes(lanesToPrewarm);
306
- }
297
+ // Nothing has been pinged. Check for lanes that need to be prewarmed.
298
+ if (!rootHasPendingCommit) {
299
+ const lanesToPrewarm = pendingLanes & ~warmLanes;
300
+ if (lanesToPrewarm !== NoLanes) {
301
+ nextLanes = getHighestPriorityLanes(lanesToPrewarm);
302
}
303
}
304
}
@@ -765,14 +760,12 @@ export function markRootSuspended(
760
root: FiberRoot,
761
suspendedLanes: Lanes,
762
spawnedLane: Lane,
768
- didAttemptEntireTree: boolean,
763
+ didSkipSuspendedSiblings: boolean,
764
) {
770
- // TODO: Split this into separate functions for marking the root at the end of
771
- // a render attempt versus suspending while the root is still in progress.
765
root.suspendedLanes |= suspendedLanes;
766
root.pingedLanes &= ~suspendedLanes;
767
775
- if (enableSiblingPrerendering && didAttemptEntireTree) {
768
+ if (!didSkipSuspendedSiblings) {
769
// Mark these lanes as warm so we know there's nothing else to work on.
770
root.warmLanes |= suspendedLanes;
771
} else {
@@ -883,7 +876,6 @@ export function markRootFinished(
876
// suspended) instead of the regular mode (i.e. unwind and skip the siblings
877
// as soon as something suspends to unblock the rest of the update).
878
if (
886
- enableSiblingPrerendering &&
879
suspendedRetryLanes !== NoLanes &&
880
// Note that we only do this if there were no updates since we started
881
// rendering. This mirrors the logic in markRootUpdated — whenever we
packages/react-reconciler/src/ReactFiberRootScheduler.js
+4
-16
@@ -18,7 +18,6 @@ import {
18
disableSchedulerTimeoutInWorkLoop,
19
enableProfilerTimer,
20
enableProfilerNestedUpdatePhase,
21
- enableSiblingPrerendering,
21
} from 'shared/ReactFeatureFlags';
22
import {
23
NoLane,
@@ -30,7 +29,6 @@ import {
29
markStarvedLanesAsExpired,
30
claimNextTransitionLane,
31
getNextLanesToFlushSync,
33
- checkIfRootIsPrerendering,
32
} from './ReactFiberLane';
33
import {
34
CommitContext,
@@ -208,10 +206,7 @@ function flushSyncWorkAcrossRoots_impl(
206
? workInProgressRootRenderLanes
207
: NoLanes,
208
);
211
- if (
212
- includesSyncLane(nextLanes) &&
213
- !checkIfRootIsPrerendering(root, nextLanes)
214
- ) {
209
+ if (includesSyncLane(nextLanes)) {
210
// This root has pending sync work. Flush it now.
211
didPerformSomeWork = true;
212
performSyncWorkOnRoot(root, nextLanes);
@@ -346,13 +341,7 @@ function scheduleTaskForRootDuringMicrotask(
341
}
342
343
// Schedule a new callback in the host environment.
349
- if (
350
- includesSyncLane(nextLanes) &&
351
- // If we're prerendering, then we should use the concurrent work loop
352
- // even if the lanes are synchronous, so that prerendering never blocks
353
- // the main thread.
354
- !(enableSiblingPrerendering && checkIfRootIsPrerendering(root, nextLanes))
355
- ) {
344
+ if (includesSyncLane(nextLanes)) {
345
// Synchronous work is always flushed at the end of the microtask, so we
346
// don't need to schedule an additional task.
347
if (existingCallbackNode !== null) {
@@ -386,10 +375,9 @@ function scheduleTaskForRootDuringMicrotask(
375
376
let schedulerPriorityLevel;
377
switch (lanesToEventPriority(nextLanes)) {
389
- // Scheduler does have an "ImmediatePriority", but now that we use
390
- // microtasks for sync work we no longer use that. Any sync work that
391
- // reaches this path is meant to be time sliced.
378
case DiscreteEventPriority:
379
+ schedulerPriorityLevel = ImmediateSchedulerPriority;
380
+ break;
381
case ContinuousEventPriority:
382
schedulerPriorityLevel = UserBlockingSchedulerPriority;
383
break;
packages/react-reconciler/src/ReactFiberWorkLoop.js
+119
-174
@@ -765,12 +765,11 @@ export function scheduleUpdateOnFiber(
765
// The incoming update might unblock the current render. Interrupt the
766
// current attempt and restart from the top.
767
prepareFreshStack(root, NoLanes);
768
- const didAttemptEntireTree = false;
768
markRootSuspended(
769
root,
770
workInProgressRootRenderLanes,
771
workInProgressDeferredLane,
773
- didAttemptEntireTree,
772
+ workInProgressRootDidSkipSuspendedSiblings,
773
);
774
}
775
@@ -833,12 +832,11 @@ export function scheduleUpdateOnFiber(
832
// effect of interrupting the current render and switching to the update.
833
// TODO: Make sure this doesn't override pings that happen while we've
834
// already started rendering.
836
- const didAttemptEntireTree = false;
835
markRootSuspended(
836
root,
837
workInProgressRootRenderLanes,
838
workInProgressDeferredLane,
841
- didAttemptEntireTree,
839
+ workInProgressRootDidSkipSuspendedSiblings,
840
);
841
}
842
}
@@ -900,120 +898,100 @@ export function performWorkOnRoot(
898
// for too long ("expired" work, to prevent starvation), or we're in
899
// sync-updates-by-default mode.
900
const shouldTimeSlice =
903
- (!forceSync &&
904
- !includesBlockingLane(lanes) &&
905
- !includesExpiredLane(root, lanes)) ||
906
- // If we're prerendering, then we should use the concurrent work loop
907
- // even if the lanes are synchronous, so that prerendering never blocks
908
- // the main thread.
909
- // TODO: We should consider doing this whenever a sync lane is suspended,
910
- // even for regular pings.
911
- (enableSiblingPrerendering && checkIfRootIsPrerendering(root, lanes));
912
-
901
+ !forceSync &&
902
+ !includesBlockingLane(lanes) &&
903
+ !includesExpiredLane(root, lanes);
904
let exitStatus = shouldTimeSlice
905
? renderRootConcurrent(root, lanes)
915
- : renderRootSync(root, lanes, true);
906
+ : renderRootSync(root, lanes);
907
917
- do {
908
+ if (exitStatus !== RootInProgress) {
909
let renderWasConcurrent = shouldTimeSlice;
919
- if (exitStatus === RootInProgress) {
920
- // Render phase is still in progress.
921
- if (
922
- enableSiblingPrerendering &&
923
- workInProgressRootIsPrerendering &&
924
- !shouldTimeSlice
925
- ) {
926
- // We're in prerendering mode, but time slicing is not enabled. This
927
- // happens when something suspends during a synchronous update. Exit the
928
- // the work loop. When we resume, we'll use the concurrent work loop so
929
- // that prerendering is non-blocking.
930
- //
931
- // Mark the root as suspended. Usually we do this at the end of the
932
- // render phase, but we do it here so that we resume in
933
- // prerendering mode.
934
- // TODO: Consider always calling markRootSuspended immediately.
935
- // Needs to be *after* we attach a ping listener, though.
936
- const didAttemptEntireTree = false;
937
- markRootSuspended(root, lanes, NoLane, didAttemptEntireTree);
938
- }
939
- break;
940
- } else if (exitStatus === RootDidNotComplete) {
941
- // The render unwound without completing the tree. This happens in special
942
- // cases where need to exit the current render without producing a
943
- // consistent tree or committing.
944
- const didAttemptEntireTree = !workInProgressRootDidSkipSuspendedSiblings;
945
- markRootSuspended(root, lanes, NoLane, didAttemptEntireTree);
946
- } else {
947
- // The render completed.
948
-
949
- // Check if this render may have yielded to a concurrent event, and if so,
950
- // confirm that any newly rendered stores are consistent.
951
- // TODO: It's possible that even a concurrent render may never have yielded
952
- // to the main thread, if it was fast enough, or if it expired. We could
953
- // skip the consistency check in that case, too.
954
- const finishedWork: Fiber = (root.current.alternate: any);
955
- if (
956
- renderWasConcurrent &&
957
- !isRenderConsistentWithExternalStores(finishedWork)
958
- ) {
959
- // A store was mutated in an interleaved event. Render again,
960
- // synchronously, to block further mutations.
961
- exitStatus = renderRootSync(root, lanes, false);
962
- // We assume the tree is now consistent because we didn't yield to any
963
- // concurrent events.
964
- renderWasConcurrent = false;
965
- // Need to check the exit status again.
966
- continue;
967
- }
968
-
969
- // Check if something threw
970
- if (
971
- (disableLegacyMode || root.tag !== LegacyRoot) &&
972
- exitStatus === RootErrored
973
- ) {
974
- const lanesThatJustErrored = lanes;
975
- const errorRetryLanes = getLanesToRetrySynchronouslyOnError(
910
+ do {
911
+ if (exitStatus === RootDidNotComplete) {
912
+ // The render unwound without completing the tree. This happens in special
913
+ // cases where need to exit the current render without producing a
914
+ // consistent tree or committing.
915
+ markRootSuspended(
916
root,
977
- lanesThatJustErrored,
917
+ lanes,
918
+ NoLane,
919
+ workInProgressRootDidSkipSuspendedSiblings,
920
);
979
- if (errorRetryLanes !== NoLanes) {
980
- lanes = errorRetryLanes;
981
- exitStatus = recoverFromConcurrentError(
921
+ } else {
922
+ // The render completed.
923
+
924
+ // Check if this render may have yielded to a concurrent event, and if so,
925
+ // confirm that any newly rendered stores are consistent.
926
+ // TODO: It's possible that even a concurrent render may never have yielded
927
+ // to the main thread, if it was fast enough, or if it expired. We could
928
+ // skip the consistency check in that case, too.
929
+ const finishedWork: Fiber = (root.current.alternate: any);
930
+ if (
931
+ renderWasConcurrent &&
932
+ !isRenderConsistentWithExternalStores(finishedWork)
933
+ ) {
934
+ // A store was mutated in an interleaved event. Render again,
935
+ // synchronously, to block further mutations.
936
+ exitStatus = renderRootSync(root, lanes);
937
+ // We assume the tree is now consistent because we didn't yield to any
938
+ // concurrent events.
939
+ renderWasConcurrent = false;
940
+ // Need to check the exit status again.
941
+ continue;
942
+ }
943
+
944
+ // Check if something threw
945
+ if (
946
+ (disableLegacyMode || root.tag !== LegacyRoot) &&
947
+ exitStatus === RootErrored
948
+ ) {
949
+ const lanesThatJustErrored = lanes;
950
+ const errorRetryLanes = getLanesToRetrySynchronouslyOnError(
951
root,
952
lanesThatJustErrored,
984
- errorRetryLanes,
953
);
986
- renderWasConcurrent = false;
987
- // Need to check the exit status again.
988
- if (exitStatus !== RootErrored) {
989
- // The root did not error this time. Restart the exit algorithm
990
- // from the beginning.
991
- // TODO: Refactor the exit algorithm to be less confusing. Maybe
992
- // more branches + recursion instead of a loop. I think the only
993
- // thing that causes it to be a loop is the RootDidNotComplete
994
- // check. If that's true, then we don't need a loop/recursion
995
- // at all.
996
- continue;
997
- } else {
998
- // The root errored yet again. Proceed to commit the tree.
954
+ if (errorRetryLanes !== NoLanes) {
955
+ lanes = errorRetryLanes;
956
+ exitStatus = recoverFromConcurrentError(
957
+ root,
958
+ lanesThatJustErrored,
959
+ errorRetryLanes,
960
+ );
961
+ renderWasConcurrent = false;
962
+ // Need to check the exit status again.
963
+ if (exitStatus !== RootErrored) {
964
+ // The root did not error this time. Restart the exit algorithm
965
+ // from the beginning.
966
+ // TODO: Refactor the exit algorithm to be less confusing. Maybe
967
+ // more branches + recursion instead of a loop. I think the only
968
+ // thing that causes it to be a loop is the RootDidNotComplete
969
+ // check. If that's true, then we don't need a loop/recursion
970
+ // at all.
971
+ continue;
972
+ } else {
973
+ // The root errored yet again. Proceed to commit the tree.
974
+ }
975
}
976
}
1001
- }
1002
- if (exitStatus === RootFatalErrored) {
1003
- prepareFreshStack(root, NoLanes);
1004
- // Since this is a fatal error, we're going to pretend we attempted
1005
- // the entire tree, to avoid scheduling a prerender.
1006
- const didAttemptEntireTree = true;
1007
- markRootSuspended(root, lanes, NoLane, didAttemptEntireTree);
1008
- break;
1009
- }
977
+ if (exitStatus === RootFatalErrored) {
978
+ prepareFreshStack(root, NoLanes);
979
+ markRootSuspended(
980
+ root,
981
+ lanes,
982
+ NoLane,
983
+ workInProgressRootDidSkipSuspendedSiblings,
984
+ );
985
+ break;
986
+ }
987
1011
- // We now have a consistent tree. The next step is either to commit it,
1012
- // or, if something suspended, wait to commit it after a timeout.
1013
- finishConcurrentRender(root, exitStatus, finishedWork, lanes);
1014
- }
1015
- break;
1016
- } while (true);
988
+ // We now have a consistent tree. The next step is either to commit it,
989
+ // or, if something suspended, wait to commit it after a timeout.
990
+ finishConcurrentRender(root, exitStatus, finishedWork, lanes);
991
+ }
992
+ break;
993
+ } while (true);
994
+ }
995
996
ensureRootIsScheduled(root);
997
}
@@ -1046,7 +1024,7 @@ function recoverFromConcurrentError(
1024
rootWorkInProgress.flags |= ForceClientRender;
1025
}
1026
1049
- const exitStatus = renderRootSync(root, errorRetryLanes, false);
1027
+ const exitStatus = renderRootSync(root, errorRetryLanes);
1028
if (exitStatus !== RootErrored) {
1029
// Successfully finished rendering on retry
1030
@@ -1130,13 +1108,11 @@ function finishConcurrentRender(
1108
// This is a transition, so we should exit without committing a
1109
// placeholder and without scheduling a timeout. Delay indefinitely
1110
// until we receive more data.
1133
- const didAttemptEntireTree =
1134
- !workInProgressRootDidSkipSuspendedSiblings;
1111
markRootSuspended(
1112
root,
1113
lanes,
1114
workInProgressDeferredLane,
1139
- didAttemptEntireTree,
1115
+ workInProgressRootDidSkipSuspendedSiblings,
1116
);
1117
return;
1118
}
@@ -1192,13 +1168,11 @@ function finishConcurrentRender(
1168
1169
// Don't bother with a very short suspense time.
1170
if (msUntilTimeout > 10) {
1195
- const didAttemptEntireTree =
1196
- !workInProgressRootDidSkipSuspendedSiblings;
1171
markRootSuspended(
1172
root,
1173
lanes,
1174
workInProgressDeferredLane,
1201
- didAttemptEntireTree,
1175
+ workInProgressRootDidSkipSuspendedSiblings,
1176
);
1177
1178
const nextLanes = getNextLanes(root, NoLanes);
@@ -1312,8 +1286,7 @@ function commitRootWhenReady(
1286
completedRenderEndTime,
1287
),
1288
);
1315
- const didAttemptEntireTree = !didSkipSuspendedSiblings;
1316
- markRootSuspended(root, lanes, spawnedLane, didAttemptEntireTree);
1289
+ markRootSuspended(root, lanes, spawnedLane, didSkipSuspendedSiblings);
1290
return;
1291
}
1292
}
@@ -1436,7 +1409,7 @@ function markRootSuspended(
1409
root: FiberRoot,
1410
suspendedLanes: Lanes,
1411
spawnedLane: Lane,
1439
- didAttemptEntireTree: boolean,
1412
+ didSkipSuspendedSiblings: boolean,
1413
) {
1414
// When suspending, we should always exclude lanes that were pinged or (more
1415
// rarely, since we try to avoid it) updated during the render phase.
@@ -1445,7 +1418,12 @@ function markRootSuspended(
1418
suspendedLanes,
1419
workInProgressRootInterleavedUpdatedLanes,
1420
);
1448
- _markRootSuspended(root, suspendedLanes, spawnedLane, didAttemptEntireTree);
1421
+ _markRootSuspended(
1422
+ root,
1423
+ suspendedLanes,
1424
+ spawnedLane,
1425
+ didSkipSuspendedSiblings,
1426
+ );
1427
}
1428
1429
export function flushRoot(root: FiberRoot, lanes: Lanes) {
@@ -1987,12 +1965,7 @@ export function renderDidSuspendDelayIfPossible(): void {
1965
1966
if (
1967
!workInProgressRootDidSkipSuspendedSiblings &&
1990
- // Check if the root will be blocked from committing.
1991
- // TODO: Consider aligning this better with the rest of the logic. Maybe
1992
- // we should only set the exit status to RootSuspendedWithDelay if this
1993
- // condition is true? And remove the equivalent checks elsewhere.
1994
- (includesOnlyTransitions(workInProgressRootRenderLanes) ||
1995
- getSuspenseHandler() === null)
1968
+ !includesBlockingLane(workInProgressRootRenderLanes)
1969
) {
1970
// This render may not have originally been scheduled as a prerender, but
1971
// something suspended inside the visible part of the tree, which means we
@@ -2018,12 +1991,11 @@ export function renderDidSuspendDelayIfPossible(): void {
1991
// pinged or updated while we were rendering.
1992
// TODO: Consider unwinding immediately, using the
1993
// SuspendedOnHydration mechanism.
2021
- const didAttemptEntireTree = false;
1994
markRootSuspended(
1995
workInProgressRoot,
1996
workInProgressRootRenderLanes,
1997
workInProgressDeferredLane,
2026
- didAttemptEntireTree,
1998
+ workInProgressRootDidSkipSuspendedSiblings,
1999
);
2000
}
2001
}
@@ -2053,11 +2025,7 @@ export function renderHasNotSuspendedYet(): boolean {
2025
// TODO: Over time, this function and renderRootConcurrent have become more
2026
// and more similar. Not sure it makes sense to maintain forked paths. Consider
2027
// unifying them again.
2056
-function renderRootSync(
2057
- root: FiberRoot,
2058
- lanes: Lanes,
2059
- shouldYieldForPrerendering: boolean,
2060
-): RootExitStatus {
2028
+function renderRootSync(root: FiberRoot, lanes: Lanes) {
2029
const prevExecutionContext = executionContext;
2030
executionContext |= RenderContext;
2031
const prevDispatcher = pushDispatcher(root.containerInfo);
@@ -2097,7 +2065,6 @@ function renderRootSync(
2065
}
2066
2067
let didSuspendInShell = false;
2100
- let exitStatus = workInProgressRootExitStatus;
2068
outer: do {
2069
try {
2070
if (
@@ -2119,37 +2086,16 @@ function renderRootSync(
2086
// Selective hydration. An update flowed into a dehydrated tree.
2087
// Interrupt the current render so the work loop can switch to the
2088
// hydration lane.
2122
- // TODO: I think we might not need to reset the stack here; we can
2123
- // just yield and reset the stack when we re-enter the work loop,
2124
- // like normal.
2089
resetWorkInProgressStack();
2126
- exitStatus = RootDidNotComplete;
2090
+ workInProgressRootExitStatus = RootDidNotComplete;
2091
break outer;
2092
}
2093
case SuspendedOnImmediate:
2130
- case SuspendedOnData:
2131
- case SuspendedOnDeprecatedThrowPromise: {
2132
- if (getSuspenseHandler() === null) {
2094
+ case SuspendedOnData: {
2095
+ if (!didSuspendInShell && getSuspenseHandler() === null) {
2096
didSuspendInShell = true;
2097
}
2135
- const reason = workInProgressSuspendedReason;
2136
- workInProgressSuspendedReason = NotSuspended;
2137
- workInProgressThrownValue = null;
2138
- throwAndUnwindWorkLoop(root, unitOfWork, thrownValue, reason);
2139
- if (
2140
- enableSiblingPrerendering &&
2141
- shouldYieldForPrerendering &&
2142
- workInProgressRootIsPrerendering
2143
- ) {
2144
- // We've switched into prerendering mode. This implies that we
2145
- // suspended outside of a Suspense boundary, which means this
2146
- // render will be blocked from committing. Yield to the main
2147
- // thread so we can switch to prerendering using the concurrent
2148
- // work loop.
2149
- exitStatus = RootInProgress;
2150
- break outer;
2151
- }
2152
- break;
2098
+ // Intentional fallthrough
2099
}
2100
default: {
2101
// Unwind then continue with the normal work loop.
@@ -2162,7 +2108,6 @@ function renderRootSync(
2108
}
2109
}
2110
workLoopSync();
2165
- exitStatus = workInProgressRootExitStatus;
2111
break;
2112
} catch (thrownValue) {
2113
handleThrow(root, thrownValue);
@@ -2185,6 +2130,14 @@ function renderRootSync(
2130
popDispatcher(prevDispatcher);
2131
popAsyncDispatcher(prevAsyncDispatcher);
2132
2133
+ if (workInProgress !== null) {
2134
+ // This is a sync render, so we should have finished the whole tree.
2135
+ throw new Error(
2136
+ 'Cannot commit an incomplete root. This error is likely caused by a ' +
2137
+ 'bug in React. Please file an issue.',
2138
+ );
2139
+ }
2140
+
2141
if (__DEV__) {
2142
if (enableDebugTracing) {
2143
logRenderStopped();
@@ -2195,21 +2148,14 @@ function renderRootSync(
2148
markRenderStopped();
2149
}
2150
2198
- if (workInProgress !== null) {
2199
- // Did not complete the tree. This can happen if something suspended in
2200
- // the shell.
2201
- } else {
2202
- // Normal case. We completed the whole tree.
2203
-
2204
- // Set this to null to indicate there's no in-progress render.
2205
- workInProgressRoot = null;
2206
- workInProgressRootRenderLanes = NoLanes;
2151
+ // Set this to null to indicate there's no in-progress render.
2152
+ workInProgressRoot = null;
2153
+ workInProgressRootRenderLanes = NoLanes;
2154
2208
- // It's safe to process the queue now that the render phase is complete.
2209
- finishQueueingConcurrentUpdates();
2210
- }
2155
+ // It's safe to process the queue now that the render phase is complete.
2156
+ finishQueueingConcurrentUpdates();
2157
2212
- return exitStatus;
2158
+ return workInProgressRootExitStatus;
2159
}
2160
2161
// The work loop is an extremely hot path. Tell Closure not to inline it.
@@ -2255,7 +2201,9 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
2201
//
2202
// If we were previously in prerendering mode, check if we received any new
2203
// data during an interleaved event.
2258
- workInProgressRootIsPrerendering = checkIfRootIsPrerendering(root, lanes);
2204
+ if (workInProgressRootIsPrerendering) {
2205
+ workInProgressRootIsPrerendering = checkIfRootIsPrerendering(root, lanes);
2206
+ }
2207
}
2208
2209
if (__DEV__) {
@@ -3805,9 +3753,6 @@ function pingSuspendedRoot(
3753
// the logic of whether or not a root suspends once it completes.
3754
// TODO: If we're rendering sync either due to Sync, Batched or expired,
3755
// we should probably never restart.
3808
- // TODO: Attach different listeners depending on whether the listener was
3809
- // attached during prerendering. Prerender pings should not interrupt
3810
- // normal renders.
3756
3757
// If we're suspended with delay, or if it's a retry, we'll always suspend
3758
// so we can always restart.
packages/react-reconciler/src/__tests__/ReactDeferredValue-test.js
-12
@@ -420,10 +420,6 @@ describe('ReactDeferredValue', () => {
420
// The initial value suspended, so we attempt the final value, which
421
// also suspends.
422
'Suspend! [Final]',
423
-
424
- ...(gate('enableSiblingPrerendering')
425
- ? ['Suspend! [Loading...]', 'Suspend! [Final]']
426
- : []),
423
]);
424
expect(root).toMatchRenderedOutput(null);
425
@@ -463,10 +459,6 @@ describe('ReactDeferredValue', () => {
459
// The initial value suspended, so we attempt the final value, which
460
// also suspends.
461
'Suspend! [Final]',
466
-
467
- ...(gate('enableSiblingPrerendering')
468
- ? ['Suspend! [Loading...]', 'Suspend! [Final]']
469
- : []),
462
]);
463
expect(root).toMatchRenderedOutput(null);
464
@@ -541,10 +533,6 @@ describe('ReactDeferredValue', () => {
533
// The initial value suspended, so we attempt the final value, which
534
// also suspends.
535
'Suspend! [Final]',
544
-
545
- ...(gate('enableSiblingPrerendering')
546
- ? ['Suspend! [Loading...]', 'Suspend! [Final]']
547
- : []),
536
]);
537
expect(root).toMatchRenderedOutput(null);
538
packages/react-reconciler/src/__tests__/ReactSiblingPrerendering-test.js
-71
@@ -479,75 +479,4 @@ describe('ReactSiblingPrerendering', () => {
479
assertLog([]);
480
},
481
);
482
-
483
- it(
484
- 'when a synchronous update suspends outside a boundary, the resulting' +
485
- 'prerender is concurrent',
486
- async () => {
487
- function App() {
488
- return (
489
- <>
490
- <Text text="A" />
491
- <Text text="B" />
492
- <AsyncText text="Async" />
493
- <Text text="C" />
494
- <Text text="D" />
495
- </>
496
- );
497
- }
498
-
499
- const root = ReactNoop.createRoot();
500
- // Mount the root synchronously
501
- ReactNoop.flushSync(() => root.render(<App />));
502
-
503
- // Synchronously render everything until we suspend in the shell
504
- assertLog(['A', 'B', 'Suspend! [Async]']);
505
-
506
- if (gate('enableSiblingPrerendering')) {
507
- // The rest of the siblings begin to prerender concurrently. Notice
508
- // that we don't unwind here; we pick up where we left off above.
509
- await waitFor(['C']);
510
- await waitFor(['D']);
511
- }
512
-
513
- assertLog([]);
514
- expect(root).toMatchRenderedOutput(null);
515
-
516
- await resolveText('Async');
517
- assertLog(['A', 'B', 'Async', 'C', 'D']);
518
- expect(root).toMatchRenderedOutput('ABAsyncCD');
519
- },
520
- );
521
-
522
- it('restart a suspended sync render if something suspends while prerendering the siblings', async () => {
523
- function App() {
524
- return (
525
- <>
526
- <Text text="A" />
527
- <Text text="B" />
528
- <AsyncText text="Async" />
529
- <Text text="C" />
530
- <Text text="D" />
531
- </>
532
- );
533
- }
534
-
535
- const root = ReactNoop.createRoot();
536
- // Mount the root synchronously
537
- ReactNoop.flushSync(() => root.render(<App />));
538
-
539
- // Synchronously render everything until we suspend in the shell
540
- assertLog(['A', 'B', 'Suspend! [Async]']);
541
-
542
- if (gate('enableSiblingPrerendering')) {
543
- // The rest of the siblings begin to prerender concurrently
544
- await waitFor(['C']);
545
- }
546
-
547
- // While we're prerendering, Async resolves. We should unwind and
548
- // start over, rather than continue prerendering D.
549
- await resolveText('Async');
550
- assertLog(['A', 'B', 'Async', 'C', 'D']);
551
- expect(root).toMatchRenderedOutput('ABAsyncCD');
552
- });
482
});