@samitouri / QOS-React / commits / 76aee6f39d

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 });