diff --git a/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js b/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js
index bffb7b96818c0..0e9cb549f653a 100644
--- a/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js
+++ b/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js
@@ -206,7 +206,12 @@ describe('ReactCache', () => {
...(gate('enableSiblingPrerendering') ? ['Invalid key type'] : []),
]);
} else {
- await waitForAll(['App', 'Loading...']);
+ await waitForAll([
+ 'App',
+ 'Loading...',
+
+ ...(gate('enableSiblingPrerendering') ? ['App'] : []),
+ ]);
}
});
@@ -226,10 +231,14 @@ describe('ReactCache', () => {
await waitForPaint(['Suspend! [1]', 'Loading...']);
jest.advanceTimersByTime(100);
assertLog(['Promise resolved [1]']);
- await waitForAll([1, 'Suspend! [2]', 1, 'Suspend! [2]', 'Suspend! [3]']);
+ await waitForAll([1, 'Suspend! [2]']);
+
+ jest.advanceTimersByTime(100);
+ assertLog(['Promise resolved [2]']);
+ await waitForAll([1, 2, 'Suspend! [3]']);
jest.advanceTimersByTime(100);
- assertLog(['Promise resolved [2]', 'Promise resolved [3]']);
+ assertLog(['Promise resolved [3]']);
await waitForAll([1, 2, 3]);
await act(() => jest.advanceTimersByTime(100));
diff --git a/packages/react-reconciler/src/ReactFiberLane.js b/packages/react-reconciler/src/ReactFiberLane.js
index 8f6f7c9c3f94f..4338d3af58546 100644
--- a/packages/react-reconciler/src/ReactFiberLane.js
+++ b/packages/react-reconciler/src/ReactFiberLane.js
@@ -233,6 +233,29 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes {
const pingedLanes = root.pingedLanes;
const warmLanes = root.warmLanes;
+ // finishedLanes represents a completed tree that is ready to commit.
+ //
+ // It's not worth doing discarding the completed tree in favor of performing
+ // speculative work. So always check this before deciding to warm up
+ // the siblings.
+ //
+ // Note that this is not set in a "suspend indefinitely" scenario, like when
+ // suspending outside of a Suspense boundary, or in the shell during a
+ // transition — only in cases where we are very likely to commit the tree in
+ // a brief amount of time (i.e. below the "Just Noticeable Difference"
+ // threshold).
+ //
+ // TODO: finishedLanes is also set when a Suspensey resource, like CSS or
+ // images, suspends during the commit phase. (We could detect that here by
+ // checking for root.cancelPendingCommit.) These are also expected to resolve
+ // quickly, because of preloading, but theoretically they could block forever
+ // like in a normal "suspend indefinitely" scenario. In the future, we should
+ // consider only blocking for up to some time limit before discarding the
+ // commit in favor of prerendering. If we do discard a pending commit, then
+ // the commit phase callback should act as a ping to try the original
+ // render again.
+ const rootHasPendingCommit = root.finishedLanes !== NoLanes;
+
// 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;
@@ -248,9 +271,11 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes {
nextLanes = getHighestPriorityLanes(nonIdlePingedLanes);
} else {
// Nothing has been pinged. Check for lanes that need to be prewarmed.
- const lanesToPrewarm = nonIdlePendingLanes & ~warmLanes;
- if (lanesToPrewarm !== NoLanes) {
- nextLanes = getHighestPriorityLanes(lanesToPrewarm);
+ if (!rootHasPendingCommit) {
+ const lanesToPrewarm = nonIdlePendingLanes & ~warmLanes;
+ if (lanesToPrewarm !== NoLanes) {
+ nextLanes = getHighestPriorityLanes(lanesToPrewarm);
+ }
}
}
}
@@ -270,9 +295,11 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes {
nextLanes = getHighestPriorityLanes(pingedLanes);
} else {
// Nothing has been pinged. Check for lanes that need to be prewarmed.
- const lanesToPrewarm = pendingLanes & ~warmLanes;
- if (lanesToPrewarm !== NoLanes) {
- nextLanes = getHighestPriorityLanes(lanesToPrewarm);
+ if (!rootHasPendingCommit) {
+ const lanesToPrewarm = pendingLanes & ~warmLanes;
+ if (lanesToPrewarm !== NoLanes) {
+ nextLanes = getHighestPriorityLanes(lanesToPrewarm);
+ }
}
}
}
diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js
index bdba41f0e8d2e..f615fc9fa34d1 100644
--- a/packages/react-reconciler/src/ReactFiberWorkLoop.js
+++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js
@@ -997,8 +997,6 @@ export function performConcurrentWorkOnRoot(
// We now have a consistent tree. The next step is either to commit it,
// or, if something suspended, wait to commit it after a timeout.
- root.finishedWork = finishedWork;
- root.finishedLanes = lanes;
finishConcurrentRender(root, exitStatus, finishedWork, lanes);
}
break;
@@ -1142,6 +1140,12 @@ function finishConcurrentRender(
}
}
+ // Only set these if we have a complete tree that is ready to be committed.
+ // We use these fields to determine later whether or not the work should be
+ // discarded for a fresh render attempt.
+ root.finishedWork = finishedWork;
+ root.finishedLanes = lanes;
+
if (shouldForceFlushFallbacksInDEV()) {
// We're inside an `act` scope. Commit immediately.
commitRoot(
@@ -1174,8 +1178,11 @@ function finishConcurrentRender(
const nextLanes = getNextLanes(root, NoLanes);
if (nextLanes !== NoLanes) {
- // There's additional work we can do on this root. We might as well
- // attempt to work on that while we're suspended.
+ // There are additional updates we can do on this root. We might as
+ // well attempt to work on that while we're suspended.
+ //
+ // Notably, this bailout does not occur if the only thing remaining is
+ // Suspense retries.
return;
}
@@ -2226,6 +2233,14 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
workInProgressTransitions = getTransitionsForLanes(root, lanes);
resetRenderTimer();
prepareFreshStack(root, lanes);
+ } else {
+ // This is a continuation of an existing work-in-progress.
+ //
+ // If we were previously in prerendering mode, check if we received any new
+ // data during an interleaved event.
+ if (workInProgressRootIsPrerendering) {
+ workInProgressRootIsPrerendering = checkIfRootIsPrerendering(root, lanes);
+ }
}
if (__DEV__) {
diff --git a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
index 89a07e8e30312..0fc64b39da899 100644
--- a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
@@ -4207,13 +4207,7 @@ describe('ReactHooksWithNoopRenderer', () => {
await act(async () => {
await resolveText('A');
});
- assertLog([
- 'Promise resolved [A]',
- 'A',
- 'Suspend! [B]',
-
- ...(gate('enableSiblingPrerendering') ? ['A', 'Suspend! [B]'] : []),
- ]);
+ assertLog(['Promise resolved [A]', 'A', 'Suspend! [B]']);
await act(() => {
root.render(null);
diff --git a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js
index 32667704b8a6f..8526fb5b19cd6 100644
--- a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js
@@ -198,11 +198,7 @@ describe('ReactLazy', () => {
await resolveFakeImport(Foo);
- await waitForAll([
- 'Foo',
-
- ...(gate('enableSiblingPrerendering') ? ['Foo'] : []),
- ]);
+ await waitForAll(['Foo']);
expect(root).not.toMatchRenderedOutput('FooBar');
await act(() => resolveFakeImport(Bar));
diff --git a/packages/react-reconciler/src/__tests__/ReactSiblingPrerendering-test.js b/packages/react-reconciler/src/__tests__/ReactSiblingPrerendering-test.js
index 902584b2851a4..6c24fdc980285 100644
--- a/packages/react-reconciler/src/__tests__/ReactSiblingPrerendering-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactSiblingPrerendering-test.js
@@ -3,7 +3,9 @@ let ReactNoop;
let Scheduler;
let act;
let assertLog;
+let waitFor;
let waitForPaint;
+let waitForAll;
let textCache;
let startTransition;
let Suspense;
@@ -18,7 +20,9 @@ describe('ReactSiblingPrerendering', () => {
Scheduler = require('scheduler');
act = require('internal-test-utils').act;
assertLog = require('internal-test-utils').assertLog;
+ waitFor = require('internal-test-utils').waitFor;
waitForPaint = require('internal-test-utils').waitForPaint;
+ waitForAll = require('internal-test-utils').waitForAll;
startTransition = React.startTransition;
Suspense = React.Suspense;
Activity = React.unstable_Activity;
@@ -26,21 +30,21 @@ describe('ReactSiblingPrerendering', () => {
textCache = new Map();
});
- // function resolveText(text) {
- // const record = textCache.get(text);
- // if (record === undefined) {
- // const newRecord = {
- // status: 'resolved',
- // value: text,
- // };
- // textCache.set(text, newRecord);
- // } else if (record.status === 'pending') {
- // const thenable = record.value;
- // record.status = 'resolved';
- // record.value = text;
- // thenable.pings.forEach(t => t());
- // }
- // }
+ function resolveText(text) {
+ const record = textCache.get(text);
+ if (record === undefined) {
+ const newRecord = {
+ status: 'resolved',
+ value: text,
+ };
+ textCache.set(text, newRecord);
+ } else if (record.status === 'pending') {
+ const thenable = record.value;
+ record.status = 'resolved';
+ record.value = text;
+ thenable.pings.forEach(t => t());
+ }
+ }
function readText(text) {
const record = textCache.get(text);
@@ -238,4 +242,231 @@ describe('ReactSiblingPrerendering', () => {
});
expect(root).toMatchRenderedOutput('Loading...');
});
+
+ it('switch back to normal rendering mode if a ping occurs during prerendering', async () => {
+ function App() {
+ return (
+
+ );
+ }
+
+ const root = ReactNoop.createRoot();
+ await act(async () => {
+ startTransition(() => root.render());
+
+ // On the first attempt, B suspends. Unwind and show a fallback, without
+ // attempting the siblings.
+ await waitForPaint(['A', 'Suspend! [B]', 'Loading outer...']);
+ expect(root).toMatchRenderedOutput(Loading outer...
);
+
+ // Now that the fallback is visible, we can prerender the siblings. Start
+ // prerendering, then yield to simulate an interleaved event.
+ if (gate('enableSiblingPrerendering')) {
+ await waitFor(['A']);
+ } else {
+ await waitForAll([]);
+ }
+
+ // To avoid the Suspense throttling mechanism, let's pretend there's been
+ // more than a Just Noticeable Difference since we rendered the
+ // outer fallback.
+ Scheduler.unstable_advanceTime(500);
+
+ // During the render phase, but before we get to B again, resolve its
+ // promise. We should re-enter normal rendering mode, but we also
+ // shouldn't unwind and lose our work-in-progress.
+ await resolveText('B');
+ await waitForPaint([
+ // When sibling prerendering is not enabled, we weren't already rendering
+ // when the data for B came in, so A doesn't get rendered until now.
+ ...(gate('enableSiblingPrerendering') ? [] : ['A']),
+
+ 'B',
+ 'Suspend! [C]',
+
+ // If we were still in prerendering mode, then we would have attempted
+ // to render D here. But since we received new data, we will skip the
+ // remaining siblings to unblock the inner fallback.
+ 'Loading inner...',
+ ]);
+
+ expect(root).toMatchRenderedOutput(
+
+
AB
+
Loading inner...
+
,
+ );
+ });
+
+ // Now that the inner fallback is showing, we can prerender the rest of
+ // the tree.
+ assertLog(
+ gate('enableSiblingPrerendering')
+ ? [
+ // NOTE: C renders twice instead of once because when B resolved, it
+ // was treated like a retry update, not just a ping. So first it
+ // regular renders, then it prerenders. TODO: We should be able to
+ // optimize this by detecting inside the retry listener that the
+ // outer boundary is no longer suspended, and therefore doesn't need
+ // to be updated.
+ 'Suspend! [C]',
+
+ // Now we're in prerender mode, so D is incuded in this attempt.
+ 'Suspend! [C]',
+ 'Suspend! [D]',
+ ]
+ : [],
+ );
+ expect(root).toMatchRenderedOutput(
+
+
AB
+
Loading inner...
+
,
+ );
+ });
+
+ it("don't throw out completed work in order to prerender", async () => {
+ function App() {
+ return (
+
+ );
+ }
+
+ const root = ReactNoop.createRoot();
+ await act(async () => {
+ startTransition(() => root.render());
+
+ await waitForPaint(['Suspend! [A]', 'Loading outer...']);
+ expect(root).toMatchRenderedOutput(Loading outer...
);
+
+ // Before the prerendering of the inner boundary starts, the data for A
+ // resolves, so we try rendering that again.
+ await resolveText('A');
+ // This produces a new tree that we can show. However, the commit phase
+ // is throttled because it's been less than a Just Noticeable Difference
+ // since the outer fallback was committed.
+ //
+ // In the meantime, we could choose to start prerendering B, but instead
+ // we wait for a JND to elapse and the commit to finish — it's not
+ // worth discarding the work we've already done.
+ await waitForAll(['A', 'Suspend! [B]', 'Loading inner...']);
+ expect(root).toMatchRenderedOutput(Loading outer...
);
+
+ // Fire the timer to commit the outer fallback.
+ jest.runAllTimers();
+ expect(root).toMatchRenderedOutput(
+ ,
+ );
+ });
+ // Once the outer fallback is committed, we can start prerendering B.
+ assertLog(gate('enableSiblingPrerendering') ? ['Suspend! [B]'] : []);
+ });
+
+ it(
+ "don't skip siblings during the retry if there was a ping since the " +
+ 'first attempt',
+ async () => {
+ function App() {
+ return (
+ <>
+
+
+
+
+ >
+ );
+ }
+
+ const root = ReactNoop.createRoot();
+ await act(async () => {
+ startTransition(() => root.render());
+
+ // On the first attempt, A suspends. Unwind and show a fallback, without
+ // attempting B or C.
+ await waitFor([
+ 'Suspend! [A]',
+ 'Loading outer...',
+
+ // Yield to simulate an interleaved event
+ ]);
+
+ // Ping the promise for A before the render phase has finished, as might
+ // happen in an interleaved network event
+ await resolveText('A');
+
+ // Now continue rendering the rest of the tree.
+ await waitForPaint(['D']);
+ expect(root).toMatchRenderedOutput(
+ <>
+ Loading outer...
+ D
+ >,
+ );
+
+ // Immediately after the fallback commits, retry the boundary again.
+ // Because the promise for A resolved, this is a normal render, _not_
+ // a prerender. So when we proceed to B, and B suspends, we unwind again
+ // without attempting C. The practical benefit of this is that we don't
+ // block the inner Suspense fallback from appearing.
+ await waitForPaint(['A', 'Suspend! [B]', 'Loading inner...']);
+ // (Since this is a retry, the commit phase is throttled by a timer.)
+ jest.runAllTimers();
+ // The inner fallback is now visible.
+ expect(root).toMatchRenderedOutput(
+ <>
+
+ D
+ >,
+ );
+
+ // Now we can proceed to prerendering C.
+ if (gate('enableSiblingPrerendering')) {
+ await waitForPaint(['Suspend! [B]', 'Suspend! [C]']);
+ }
+ });
+ assertLog([]);
+ },
+ );
});
diff --git a/packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js b/packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js
index 18dd6820e3f85..d7ad652f1da7e 100644
--- a/packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js
@@ -183,7 +183,7 @@ describe('ReactSuspense', () => {
'A',
...(gate('enableSiblingPrerendering')
- ? ['Suspend! [B]', 'A', 'Suspend! [B]']
+ ? ['Suspend! [B]', 'Suspend! [B]']
: []),
]);
expect(container.textContent).toEqual('ALoading B...');
@@ -296,15 +296,7 @@ describe('ReactSuspense', () => {
expect(container.textContent).toEqual('Loading...');
await resolveText('A');
- await waitForAll([
- 'A',
- 'Suspend! [B]',
- 'Loading more...',
-
- ...(gate('enableSiblingPrerendering')
- ? ['A', 'Suspend! [B]', 'Loading more...']
- : []),
- ]);
+ await waitForAll(['A', 'Suspend! [B]', 'Loading more...']);
// By this point, we have enough info to show "A" and "Loading more..."
// However, we've just shown the outer fallback. So we'll delay
@@ -364,14 +356,7 @@ describe('ReactSuspense', () => {
// B starts loading. Parent boundary is in throttle.
// Still shows parent loading under throttle
jest.advanceTimersByTime(10);
- await waitForAll([
- 'Suspend! [B]',
- 'Loading more...',
-
- ...(gate('enableSiblingPrerendering')
- ? ['A', 'Suspend! [B]', 'Loading more...']
- : []),
- ]);
+ await waitForAll(['Suspend! [B]', 'Loading more...']);
expect(container.textContent).toEqual('Loading...');
// !! B could have finished before the throttle, but we show a fallback.
@@ -413,15 +398,7 @@ describe('ReactSuspense', () => {
expect(container.textContent).toEqual('Loading...');
await resolveText('A');
- await waitForAll([
- 'A',
- 'Suspend! [B]',
- 'Loading more...',
-
- ...(gate('enableSiblingPrerendering')
- ? ['A', 'Suspend! [B]', 'Loading more...']
- : []),
- ]);
+ await waitForAll(['A', 'Suspend! [B]', 'Loading more...']);
// By this point, we have enough info to show "A" and "Loading more..."
// However, we've just shown the outer fallback. So we'll delay
@@ -766,14 +743,7 @@ describe('ReactSuspense', () => {
: []),
]);
await resolveText('Child 1');
- await waitForAll([
- 'Child 1',
- 'Suspend! [Child 2]',
-
- ...(gate('enableSiblingPrerendering')
- ? ['Child 1', 'Suspend! [Child 2]']
- : []),
- ]);
+ await waitForAll(['Child 1', 'Suspend! [Child 2]']);
jest.advanceTimersByTime(6000);
diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseCallback-test.js b/packages/react-reconciler/src/__tests__/ReactSuspenseCallback-test.js
index 7f3724fa86490..6da6ef7573fd9 100644
--- a/packages/react-reconciler/src/__tests__/ReactSuspenseCallback-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactSuspenseCallback-test.js
@@ -136,7 +136,11 @@ describe('ReactSuspense', () => {
ReactNoop.render(element);
await waitForAll([]);
expect(ReactNoop).toMatchRenderedOutput('Waiting Tier 1');
- expect(ops).toEqual([new Set([promise2])]);
+ expect(ops).toEqual([
+ new Set([promise2]),
+
+ ...(gate('enableSiblingPrerendering') ? new Set([promise2]) : []),
+ ]);
ops = [];
await act(() => resolve2());
@@ -224,7 +228,11 @@ describe('ReactSuspense', () => {
await act(() => resolve1());
expect(ReactNoop).toMatchRenderedOutput('Waiting Tier 2Done');
expect(ops1).toEqual([]);
- expect(ops2).toEqual([new Set([promise2])]);
+ expect(ops2).toEqual([
+ new Set([promise2]),
+
+ ...(gate('enableSiblingPrerendering') ? new Set([promise2]) : []),
+ ]);
ops1 = [];
ops2 = [];
diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseEffectsSemantics-test.js b/packages/react-reconciler/src/__tests__/ReactSuspenseEffectsSemantics-test.js
index 4ade3c6ff79cf..4a094dc7fed43 100644
--- a/packages/react-reconciler/src/__tests__/ReactSuspenseEffectsSemantics-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactSuspenseEffectsSemantics-test.js
@@ -1268,22 +1268,16 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Suspend:InnerAsync_2',
'Text:InnerFallback render',
- ...(gate('enableSiblingPrerendering')
- ? [
- 'Text:Outer render',
- 'AsyncText:OuterAsync_1 render',
- 'Text:Inner render',
- 'Suspend:InnerAsync_2',
- 'Text:InnerFallback render',
- ]
- : []),
-
'Text:OuterFallback destroy layout',
'Text:Outer create layout',
'AsyncText:OuterAsync_1 create layout',
'Text:InnerFallback create layout',
'Text:OuterFallback destroy passive',
'AsyncText:OuterAsync_1 create passive',
+
+ ...(gate('enableSiblingPrerendering')
+ ? ['Text:Inner render', 'Suspend:InnerAsync_2']
+ : []),
]);
expect(ReactNoop).toMatchRenderedOutput(
<>
diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseList-test.js b/packages/react-reconciler/src/__tests__/ReactSuspenseList-test.js
index 59388050f2ade..b4a7bcc186b6d 100644
--- a/packages/react-reconciler/src/__tests__/ReactSuspenseList-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactSuspenseList-test.js
@@ -255,7 +255,7 @@ describe('ReactSuspenseList', () => {
await act(() => C.resolve());
assertLog(
gate('enableSiblingPrerendering')
- ? ['Suspend! [B]', 'C', 'Suspend! [B]', 'C']
+ ? ['Suspend! [B]', 'C', 'Suspend! [B]']
: ['C'],
);
@@ -390,13 +390,7 @@ describe('ReactSuspenseList', () => {
);
await act(() => B.resolve());
- assertLog([
- 'A',
- 'B',
- 'Suspend! [C]',
-
- ...(gate('enableSiblingPrerendering') ? ['A', 'B', 'Suspend! [C]'] : []),
- ]);
+ assertLog(['A', 'B', 'Suspend! [C]']);
expect(ReactNoop).toMatchRenderedOutput(
<>
@@ -472,13 +466,7 @@ describe('ReactSuspenseList', () => {
);
await act(() => B.resolve());
- assertLog([
- 'A',
- 'B',
- 'Suspend! [C]',
-
- ...(gate('enableSiblingPrerendering') ? ['A', 'B', 'Suspend! [C]'] : []),
- ]);
+ assertLog(['A', 'B', 'Suspend! [C]']);
expect(ReactNoop).toMatchRenderedOutput(
<>
@@ -749,7 +737,11 @@ describe('ReactSuspenseList', () => {
ReactNoop.render();
- await waitForAll(['Suspend! [A]', 'Loading']);
+ await waitForAll([
+ 'Suspend! [A]',
+ 'Loading',
+ ...(gate('enableSiblingPrerendering') ? ['Suspend! [A]'] : []),
+ ]);
expect(ReactNoop).toMatchRenderedOutput(Loading);
@@ -786,12 +778,7 @@ describe('ReactSuspenseList', () => {
);
await act(() => B.resolve());
- assertLog([
- 'B',
- 'Suspend! [C]',
-
- ...(gate('enableSiblingPrerendering') ? ['B', 'Suspend! [C]'] : []),
- ]);
+ assertLog(['B', 'Suspend! [C]']);
// Even though we could now show B, we're still waiting on C.
expect(ReactNoop).toMatchRenderedOutput(
@@ -878,12 +865,7 @@ describe('ReactSuspenseList', () => {
expect(ReactNoop).toMatchRenderedOutput(A);
await act(() => B.resolve());
- assertLog([
- 'B',
- 'Suspend! [C]',
-
- ...(gate('enableSiblingPrerendering') ? ['B', 'Suspend! [C]'] : []),
- ]);
+ assertLog(['B', 'Suspend! [C]']);
// Even though we could now show B, we're still waiting on C.
expect(ReactNoop).toMatchRenderedOutput(A);
@@ -948,7 +930,7 @@ describe('ReactSuspenseList', () => {
'A',
'Suspend! [B]',
- ...(gate('enableSiblingPrerendering') ? ['A', 'Suspend! [B]'] : []),
+ ...(gate('enableSiblingPrerendering') ? ['Suspend! [B]'] : []),
]);
expect(ReactNoop).toMatchRenderedOutput(
@@ -1019,7 +1001,7 @@ describe('ReactSuspenseList', () => {
'C',
'Suspend! [B]',
- ...(gate('enableSiblingPrerendering') ? ['C', 'Suspend! [B]'] : []),
+ ...(gate('enableSiblingPrerendering') ? ['Suspend! [B]'] : []),
]);
expect(ReactNoop).toMatchRenderedOutput(
@@ -1125,12 +1107,7 @@ describe('ReactSuspenseList', () => {
);
await act(() => A.resolve());
- assertLog([
- 'A',
- 'Suspend! [C]',
-
- ...(gate('enableSiblingPrerendering') ? ['A', 'Suspend! [C]'] : []),
- ]);
+ assertLog(['A', 'Suspend! [C]']);
// Even though we could show A, it is still in a fallback state because
// C is not yet resolved. We need to resolve everything in the head first.
@@ -1151,7 +1128,7 @@ describe('ReactSuspenseList', () => {
'C',
'Suspend! [E]',
- ...(gate('enableSiblingPrerendering') ? ['A', 'C', 'Suspend! [E]'] : []),
+ ...(gate('enableSiblingPrerendering') ? ['Suspend! [E]'] : []),
]);
// We can now resolve the full head.
@@ -1171,7 +1148,7 @@ describe('ReactSuspenseList', () => {
'E',
'Suspend! [F]',
- ...(gate('enableSiblingPrerendering') ? ['E', 'Suspend! [F]'] : []),
+ ...(gate('enableSiblingPrerendering') ? ['Suspend! [F]'] : []),
]);
// In the tail we can resolve one-by-one.
@@ -1340,12 +1317,7 @@ describe('ReactSuspenseList', () => {
await F.resolve();
- await waitForAll([
- 'Suspend! [D]',
- 'F',
-
- ...(gate('enableSiblingPrerendering') ? ['Suspend! [D]', 'F'] : []),
- ]);
+ await waitForAll(['Suspend! [D]', 'F']);
// Even though we could show F, it is still in a fallback state because
// E is not yet resolved. We need to resolve everything in the head first.
@@ -1370,7 +1342,7 @@ describe('ReactSuspenseList', () => {
'F',
'Suspend! [B]',
- ...(gate('enableSiblingPrerendering') ? ['D', 'F', 'Suspend! [B]'] : []),
+ ...(gate('enableSiblingPrerendering') ? ['Suspend! [B]'] : []),
]);
// We can now resolve the full head.
@@ -1392,7 +1364,7 @@ describe('ReactSuspenseList', () => {
'B',
'Suspend! [A]',
- ...(gate('enableSiblingPrerendering') ? ['B', 'Suspend! [A]'] : []),
+ ...(gate('enableSiblingPrerendering') ? ['Suspend! [A]'] : []),
]);
// In the tail we can resolve one-by-one.
@@ -1523,15 +1495,7 @@ describe('ReactSuspenseList', () => {
await A.resolve();
- await waitForAll([
- 'A',
- 'Suspend! [B]',
- 'Loading B',
-
- ...(gate('enableSiblingPrerendering')
- ? ['A', 'Suspend! [B]', 'Loading B']
- : []),
- ]);
+ await waitForAll(['A', 'Suspend! [B]', 'Loading B']);
// Incremental loading is suspended.
jest.advanceTimersByTime(500);
@@ -1545,15 +1509,7 @@ describe('ReactSuspenseList', () => {
await B.resolve();
- await waitForAll([
- 'B',
- 'Suspend! [C]',
- 'Loading C',
-
- ...(gate('enableSiblingPrerendering')
- ? ['B', 'Suspend! [C]', 'Loading C']
- : []),
- ]);
+ await waitForAll(['B', 'Suspend! [C]', 'Loading C']);
// Incremental loading is suspended.
jest.advanceTimersByTime(500);
@@ -1777,12 +1733,7 @@ describe('ReactSuspenseList', () => {
await B.resolve();
- await waitForAll([
- 'B',
- 'Suspend! [C]',
-
- ...(gate('enableSiblingPrerendering') ? ['B', 'Suspend! [C]'] : []),
- ]);
+ await waitForAll(['B', 'Suspend! [C]']);
// Incremental loading is suspended.
jest.advanceTimersByTime(500);
@@ -1802,17 +1753,7 @@ describe('ReactSuspenseList', () => {
await C.resolve();
await E.resolve();
- await waitForAll([
- 'B',
- 'C',
- 'E',
- 'Suspend! [F]',
- 'Loading F',
-
- ...(gate('enableSiblingPrerendering')
- ? ['B', 'C', 'E', 'Suspend! [F]', 'Loading F']
- : []),
- ]);
+ await waitForAll(['B', 'C', 'E', 'Suspend! [F]', 'Loading F']);
jest.advanceTimersByTime(500);
@@ -1929,12 +1870,7 @@ describe('ReactSuspenseList', () => {
await D.resolve();
- await waitForAll([
- 'D',
- 'Suspend! [E]',
-
- ...(gate('enableSiblingPrerendering') ? ['D', 'Suspend! [E]'] : []),
- ]);
+ await waitForAll(['D', 'Suspend! [E]']);
// Incremental loading is suspended.
jest.advanceTimersByTime(500);
@@ -1959,17 +1895,7 @@ describe('ReactSuspenseList', () => {
await D.resolve();
await E.resolve();
- await waitForAll([
- 'D',
- 'E',
- 'B',
- 'Suspend! [A]',
- 'Loading A',
-
- ...(gate('enableSiblingPrerendering')
- ? ['D', 'E', 'B', 'Suspend! [A]', 'Loading A']
- : []),
- ]);
+ await waitForAll(['D', 'E', 'B', 'Suspend! [A]', 'Loading A']);
jest.advanceTimersByTime(500);
@@ -2100,12 +2026,7 @@ describe('ReactSuspenseList', () => {
await B.resolve();
- await waitForAll([
- 'B',
- 'Suspend! [C]',
-
- ...(gate('enableSiblingPrerendering') ? ['B', 'Suspend! [C]'] : []),
- ]);
+ await waitForAll(['B', 'Suspend! [C]']);
// Incremental loading is suspended.
jest.advanceTimersByTime(500);
@@ -2128,17 +2049,7 @@ describe('ReactSuspenseList', () => {
await D.resolve();
await E.resolve();
- await waitForAll([
- 'C',
- 'D',
- 'E',
- 'Suspend! [F]',
- 'Loading F',
-
- ...(gate('enableSiblingPrerendering')
- ? ['C', 'D', 'E', 'Suspend! [F]', 'Loading F']
- : []),
- ]);
+ await waitForAll(['C', 'D', 'E', 'Suspend! [F]', 'Loading F']);
jest.advanceTimersByTime(500);
@@ -2201,15 +2112,7 @@ describe('ReactSuspenseList', () => {
await A.resolve();
- await waitForAll([
- 'A',
- 'Suspend! [B]',
- 'Loading B',
-
- ...(gate('enableSiblingPrerendering')
- ? ['A', 'Suspend! [B]', 'Loading B']
- : []),
- ]);
+ await waitForAll(['A', 'Suspend! [B]', 'Loading B']);
// Incremental loading is suspended.
jest.advanceTimersByTime(500);
@@ -2217,15 +2120,7 @@ describe('ReactSuspenseList', () => {
expect(ReactNoop).toMatchRenderedOutput(A);
await act(() => B.resolve());
- assertLog([
- 'B',
- 'Suspend! [C]',
- 'Loading C',
-
- ...(gate('enableSiblingPrerendering')
- ? ['B', 'Suspend! [C]', 'Loading C']
- : []),
- ]);
+ assertLog(['B', 'Suspend! [C]', 'Loading C']);
// Incremental loading is suspended.
jest.advanceTimersByTime(500);
@@ -2987,7 +2882,7 @@ describe('ReactSuspenseList', () => {
'C',
'Suspend! [D]',
- ...(gate('enableSiblingPrerendering') ? ['C', 'Suspend! [D]'] : []),
+ ...(gate('enableSiblingPrerendering') ? ['Suspend! [D]'] : []),
]);
expect(ReactNoop).toMatchRenderedOutput(
<>
@@ -2999,12 +2894,12 @@ describe('ReactSuspenseList', () => {
);
if (gate('enableSiblingPrerendering')) {
- expect(onRender).toHaveBeenCalledTimes(5);
+ expect(onRender).toHaveBeenCalledTimes(6);
// actualDuration
- expect(onRender.mock.calls[4][2]).toBe(5 + 12);
+ expect(onRender.mock.calls[5][2]).toBe(12);
// treeBaseDuration
- expect(onRender.mock.calls[4][3]).toBe(1 + 4 + 5 + 3);
+ expect(onRender.mock.calls[5][3]).toBe(1 + 4 + 5 + 3);
} else {
expect(onRender).toHaveBeenCalledTimes(4);
@@ -3095,7 +2990,7 @@ describe('ReactSuspenseList', () => {
'A',
'Suspend! [B]',
- ...(gate('enableSiblingPrerendering') ? ['A', 'Suspend! [B]'] : []),
+ ...(gate('enableSiblingPrerendering') ? ['Suspend! [B]'] : []),
]);
expect(ReactNoop).toMatchRenderedOutput(
<>
diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js
index f834dd8912a34..39cfebec3487e 100644
--- a/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js
@@ -353,7 +353,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
'A',
...(gate('enableSiblingPrerendering')
- ? ['Suspend! [B]', 'A', 'Suspend! [B]']
+ ? ['Suspend! [B]', 'Suspend! [B]']
: []),
]);
expect(ReactNoop).toMatchRenderedOutput(
@@ -788,10 +788,6 @@ describe('ReactSuspenseWithNoopRenderer', () => {
'Outer content',
'Suspend! [Inner content]',
'Loading inner...',
-
- ...(gate('enableSiblingPrerendering')
- ? ['Outer content', 'Suspend! [Inner content]', 'Loading inner...']
- : []),
]);
// Don't commit the inner placeholder yet.
expect(ReactNoop).toMatchRenderedOutput(
@@ -1819,10 +1815,6 @@ describe('ReactSuspenseWithNoopRenderer', () => {
// B suspends
'Suspend! [B]',
'Loading more...',
-
- ...(gate('enableSiblingPrerendering')
- ? ['A', 'Suspend! [B]', 'Loading more...']
- : []),
]);
// Because we've already been waiting for so long we can
// wait a bit longer. Still nothing...
@@ -3698,10 +3690,6 @@ describe('ReactSuspenseWithNoopRenderer', () => {
);
});
- // This regression test relies on subtle implementation details that happen to
- // rely on sibling prerendering being disabled. Not going to bother to rewrite
- // it for now; maybe once we land the experiment.
- // @gate !enableSiblingPrerendering
// @gate enableLegacyCache
it('regression: ping at high priority causes update to be dropped', async () => {
const {useState, useTransition} = React;
diff --git a/packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js b/packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js
index c6d79ed76c20a..7a850325e9f2c 100644
--- a/packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js
@@ -2446,11 +2446,10 @@ describe('ReactInteractionTracing', () => {
'Text',
'Suspend [Text Two]',
'Loading Two...',
- ...(gate('enableSiblingPrerendering')
- ? ['Suspend [Text Two]', 'Suspend [Text Two]']
- : []),
+ ...(gate('enableSiblingPrerendering') ? ['Suspend [Text Two]'] : []),
'onTransitionStart(transition, 0)',
'onTransitionProgress(transition, 0, 1000, [two])',
+ ...(gate('enableSiblingPrerendering') ? ['Suspend [Text Two]'] : []),
]);
await act(() => {
diff --git a/packages/react-reconciler/src/__tests__/ReactUse-test.js b/packages/react-reconciler/src/__tests__/ReactUse-test.js
index 66c2e116a5372..97d1df55d57dc 100644
--- a/packages/react-reconciler/src/__tests__/ReactUse-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactUse-test.js
@@ -1077,25 +1077,13 @@ describe('ReactUse', () => {
await act(() => {
resolveTextRequests('A');
});
- assertLog([
- 'A',
- '(Loading B...)',
-
- ...(gate('enableSiblingPrerendering')
- ? ['A', '(Loading C...)', '(Loading B...)']
- : []),
- ]);
+ assertLog(['A', '(Loading B...)']);
expect(root).toMatchRenderedOutput('A(Loading B...)');
await act(() => {
resolveTextRequests('B');
});
- assertLog([
- 'B',
- '(Loading C...)',
-
- ...(gate('enableSiblingPrerendering') ? ['B', '(Loading C...)'] : []),
- ]);
+ assertLog(['B', '(Loading C...)']);
expect(root).toMatchRenderedOutput('AB(Loading C...)');
await act(() => {