From a8b362381f2db9ccdfd479135b52e624c7c64e3a Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 4 Sep 2024 14:28:55 -0400 Subject: [PATCH] Prerender during same pass if blocked anyway MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If something suspends in the shell — i.e. we won't replace the suspended content with a fallback — we might as well prerender the siblings during the current render pass, instead of spawning a separate prerender pass. This is implemented by setting the "is prerendering" flag to true whenever we suspend in the shell. But only if we haven't already skipped over some siblings, because if so, then we need to schedule a separate prerender pass regardless. --- .../__tests__/ReactCacheOld-test.internal.js | 4 + .../src/__tests__/ReactDOMFiberAsync-test.js | 2 +- .../src/__tests__/ReactDOMForm-test.js | 49 +--- .../react-reconciler/src/ReactFiberHooks.js | 4 +- .../react-reconciler/src/ReactFiberLane.js | 2 +- .../src/ReactFiberWorkLoop.js | 14 +- .../src/__tests__/ActivitySuspense-test.js | 20 +- .../src/__tests__/ReactActWarnings-test.js | 9 +- .../src/__tests__/ReactAsyncActions-test.js | 21 +- .../ReactConcurrentErrorRecovery-test.js | 49 +--- .../src/__tests__/ReactDeferredValue-test.js | 10 - .../src/__tests__/ReactExpiration-test.js | 7 +- .../ReactHooksWithNoopRenderer-test.js | 43 +--- .../src/__tests__/ReactLazy-test.internal.js | 14 +- .../__tests__/ReactSuspense-test.internal.js | 7 +- .../ReactSuspenseWithNoopRenderer-test.js | 238 +++--------------- .../src/__tests__/ReactTransition-test.js | 80 +----- .../src/__tests__/ReactUse-test.js | 8 - .../src/__tests__/useMemoCache-test.js | 32 +-- .../__tests__/useSyncExternalStore-test.js | 4 +- 20 files changed, 96 insertions(+), 521 deletions(-) diff --git a/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js b/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js index 7589463c49e7b..bec901c722007 100644 --- a/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js +++ b/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js @@ -238,12 +238,14 @@ describe('ReactCache', () => { await act(() => jest.advanceTimersByTime(100)); assertLog([ 'Promise resolved [4]', + 1, 4, 'Suspend! [5]', 1, 4, 'Suspend! [5]', + 'Promise resolved [5]', 1, 4, @@ -274,12 +276,14 @@ describe('ReactCache', () => { await act(() => jest.advanceTimersByTime(100)); assertLog([ 'Promise resolved [2]', + 1, 2, 'Suspend! [3]', 1, 2, 'Suspend! [3]', + 'Promise resolved [3]', 1, 2, diff --git a/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js b/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js index 027099d54707c..ee843996bef1c 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js @@ -744,7 +744,7 @@ describe('ReactDOMFiberAsync', () => { // Because it suspended, it remains on the current path expect(div.textContent).toBe('/path/a'); }); - assertLog(gate('enableSiblingPrerendering') ? ['Suspend! [/path/b]'] : []); + assertLog([]); await act(async () => { resolvePromise(); diff --git a/packages/react-dom/src/__tests__/ReactDOMForm-test.js b/packages/react-dom/src/__tests__/ReactDOMForm-test.js index b2a45a4c71b1b..5a44cb6afec09 100644 --- a/packages/react-dom/src/__tests__/ReactDOMForm-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMForm-test.js @@ -699,15 +699,7 @@ describe('ReactDOMForm', () => { // This should suspend because form actions are implicitly wrapped // in startTransition. await submit(formRef.current); - assertLog([ - 'Pending...', - 'Suspend! [Updated]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [Updated]', 'Loading...'] - : []), - ]); + assertLog(['Pending...', 'Suspend! [Updated]', 'Loading...']); expect(container.textContent).toBe('Pending...Initial'); await act(() => resolveText('Updated')); @@ -744,15 +736,7 @@ describe('ReactDOMForm', () => { // Update await submit(formRef.current); - assertLog([ - 'Pending...', - 'Suspend! [Count: 1]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [Count: 1]', 'Loading...'] - : []), - ]); + assertLog(['Pending...', 'Suspend! [Count: 1]', 'Loading...']); expect(container.textContent).toBe('Pending...Count: 0'); await act(() => resolveText('Count: 1')); @@ -761,15 +745,7 @@ describe('ReactDOMForm', () => { // Update again await submit(formRef.current); - assertLog([ - 'Pending...', - 'Suspend! [Count: 2]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [Count: 2]', 'Loading...'] - : []), - ]); + assertLog(['Pending...', 'Suspend! [Count: 2]', 'Loading...']); expect(container.textContent).toBe('Pending...Count: 1'); await act(() => resolveText('Count: 2')); @@ -813,14 +789,7 @@ describe('ReactDOMForm', () => { assertLog(['Async action started', 'Pending...']); await act(() => resolveText('Wait')); - assertLog([ - 'Suspend! [Updated]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [Updated]', 'Loading...'] - : []), - ]); + assertLog(['Suspend! [Updated]', 'Loading...']); expect(container.textContent).toBe('Pending...Initial'); await act(() => resolveText('Updated')); @@ -1506,15 +1475,7 @@ describe('ReactDOMForm', () => { // Now dispatch inside of a transition. This one does not trigger a // loading state. await act(() => startTransition(() => dispatch())); - assertLog([ - 'Count: 1', - 'Suspend! [Count: 2]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [Count: 2]', 'Loading...'] - : []), - ]); + assertLog(['Count: 1', 'Suspend! [Count: 2]', 'Loading...']); expect(container.textContent).toBe('Count: 1'); await act(() => resolveText('Count: 2')); diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js index 83b9d10fbf91d..daca1ffa7c2f4 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.js +++ b/packages/react-reconciler/src/ReactFiberHooks.js @@ -1666,7 +1666,7 @@ function mountSyncExternalStore( } const rootRenderLanes = getWorkInProgressRootRenderLanes(); - if (!includesBlockingLane(root, rootRenderLanes)) { + if (!includesBlockingLane(rootRenderLanes)) { pushStoreConsistencyCheck(fiber, getSnapshot, nextSnapshot); } } @@ -1778,7 +1778,7 @@ function updateSyncExternalStore( ); } - if (!isHydrating && !includesBlockingLane(root, renderLanes)) { + if (!isHydrating && !includesBlockingLane(renderLanes)) { pushStoreConsistencyCheck(fiber, getSnapshot, nextSnapshot); } } diff --git a/packages/react-reconciler/src/ReactFiberLane.js b/packages/react-reconciler/src/ReactFiberLane.js index 2995548fbbee2..fe36ce548b33a 100644 --- a/packages/react-reconciler/src/ReactFiberLane.js +++ b/packages/react-reconciler/src/ReactFiberLane.js @@ -579,7 +579,7 @@ export function includesOnlyTransitions(lanes: Lanes): boolean { return (lanes & TransitionLanes) === lanes; } -export function includesBlockingLane(root: FiberRoot, lanes: Lanes): boolean { +export function includesBlockingLane(lanes: Lanes): boolean { const SyncDefaultLanes = InputContinuousHydrationLane | InputContinuousLane | diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index 69546293d3203..348ca757d2d6d 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -907,7 +907,7 @@ export function performConcurrentWorkOnRoot( // bug we're still investigating. Once the bug in Scheduler is fixed, // we can remove this, since we track expiration ourselves. const shouldTimeSlice = - !includesBlockingLane(root, lanes) && + !includesBlockingLane(lanes) && !includesExpiredLane(root, lanes) && (disableSchedulerTimeoutInWorkLoop || !didTimeout); let exitStatus = shouldTimeSlice @@ -1968,6 +1968,18 @@ export function renderDidSuspend(): void { export function renderDidSuspendDelayIfPossible(): void { workInProgressRootExitStatus = RootSuspendedWithDelay; + if ( + !workInProgressRootDidSkipSuspendedSiblings && + !includesBlockingLane(workInProgressRootRenderLanes) + ) { + // This render may not have originally been scheduled as a prerender, but + // something suspended inside the visible part of the tree, which means we + // won't be able to commit a fallback anyway. Let's proceed as if this were + // a prerender so that we can warm up the siblings without scheduling a + // separate pass. + workInProgressRootIsPrerendering = true; + } + // Check if there are updates that we skipped tree that might have unblocked // this render. if ( diff --git a/packages/react-reconciler/src/__tests__/ActivitySuspense-test.js b/packages/react-reconciler/src/__tests__/ActivitySuspense-test.js index c2a29c8f2139f..d02ae70e523ca 100644 --- a/packages/react-reconciler/src/__tests__/ActivitySuspense-test.js +++ b/packages/react-reconciler/src/__tests__/ActivitySuspense-test.js @@ -215,15 +215,7 @@ describe('Activity Suspense', () => { ); }); }); - assertLog([ - 'Open', - 'Suspend! [Async]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Open', 'Suspend! [Async]', 'Loading...'] - : []), - ]); + assertLog(['Open', 'Suspend! [Async]', 'Loading...']); // It should suspend with delay to prevent the already-visible Suspense // boundary from switching to a fallback expect(root).toMatchRenderedOutput(Closed); @@ -284,15 +276,7 @@ describe('Activity Suspense', () => { ); }); }); - assertLog([ - 'Open', - 'Suspend! [Async]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Open', 'Suspend! [Async]', 'Loading...'] - : []), - ]); + assertLog(['Open', 'Suspend! [Async]', 'Loading...']); // It should suspend with delay to prevent the already-visible Suspense // boundary from switching to a fallback expect(root).toMatchRenderedOutput( diff --git a/packages/react-reconciler/src/__tests__/ReactActWarnings-test.js b/packages/react-reconciler/src/__tests__/ReactActWarnings-test.js index 21fb6727527ab..03e04e8b1dc40 100644 --- a/packages/react-reconciler/src/__tests__/ReactActWarnings-test.js +++ b/packages/react-reconciler/src/__tests__/ReactActWarnings-test.js @@ -349,14 +349,7 @@ describe('act warnings', () => { root.render(); }); }); - assertLog([ - 'Suspend! [Async]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [Async]', 'Loading...'] - : []), - ]); + assertLog(['Suspend! [Async]', 'Loading...']); expect(root).toMatchRenderedOutput('(empty)'); // This is a ping, not a retry, because no fallback is showing. diff --git a/packages/react-reconciler/src/__tests__/ReactAsyncActions-test.js b/packages/react-reconciler/src/__tests__/ReactAsyncActions-test.js index 4cf830876b642..2a65660b20f4b 100644 --- a/packages/react-reconciler/src/__tests__/ReactAsyncActions-test.js +++ b/packages/react-reconciler/src/__tests__/ReactAsyncActions-test.js @@ -303,7 +303,7 @@ describe('ReactAsyncActions', () => { 'Suspend! [A1]', ...(gate('enableSiblingPrerendering') - ? ['Pending: false', 'Suspend! [A1]', 'Suspend! [B1]', 'Suspend! [C1]'] + ? ['Suspend! [B1]', 'Suspend! [C1]'] : []), ]); expect(root).toMatchRenderedOutput( @@ -322,9 +322,7 @@ describe('ReactAsyncActions', () => { 'A1', 'Suspend! [B1]', - ...(gate('enableSiblingPrerendering') - ? ['Pending: false', 'A1', 'Suspend! [B1]', 'Suspend! [C1]'] - : []), + ...(gate('enableSiblingPrerendering') ? ['Suspend! [C1]'] : []), ]); expect(root).toMatchRenderedOutput( <> @@ -333,16 +331,7 @@ describe('ReactAsyncActions', () => { , ); await act(() => resolveText('B1')); - assertLog([ - 'Pending: false', - 'A1', - 'B1', - 'Suspend! [C1]', - - ...(gate('enableSiblingPrerendering') - ? ['Pending: false', 'A1', 'B1', 'Suspend! [C1]'] - : []), - ]); + assertLog(['Pending: false', 'A1', 'B1', 'Suspend! [C1]']); expect(root).toMatchRenderedOutput( <> Pending: true @@ -715,10 +704,6 @@ describe('ReactAsyncActions', () => { // automatically reverted. 'Pending: false', 'Suspend! [B]', - - ...(gate('enableSiblingPrerendering') - ? ['Pending: false', 'Suspend! [B]'] - : []), ]); // Resolve the transition diff --git a/packages/react-reconciler/src/__tests__/ReactConcurrentErrorRecovery-test.js b/packages/react-reconciler/src/__tests__/ReactConcurrentErrorRecovery-test.js index cec34dc722799..db9d7c336ec2e 100644 --- a/packages/react-reconciler/src/__tests__/ReactConcurrentErrorRecovery-test.js +++ b/packages/react-reconciler/src/__tests__/ReactConcurrentErrorRecovery-test.js @@ -209,16 +209,7 @@ describe('ReactConcurrentErrorRecovery', () => { root.render(); }); }); - assertLog([ - 'Suspend! [A2]', - 'Loading...', - 'Suspend! [B2]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [A2]', 'Loading...', 'Suspend! [B2]', 'Loading...'] - : []), - ]); + assertLog(['Suspend! [A2]', 'Loading...', 'Suspend! [B2]', 'Loading...']); // Because this is a refresh, we don't switch to a fallback expect(root).toMatchRenderedOutput('A1B1'); @@ -229,16 +220,7 @@ describe('ReactConcurrentErrorRecovery', () => { // Because we're still suspended on A, we can't show an error boundary. We // should wait for A to resolve. - assertLog([ - 'Suspend! [A2]', - 'Loading...', - 'Error! [B2]', - 'Oops!', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [A2]', 'Loading...', 'Error! [B2]', 'Oops!'] - : []), - ]); + assertLog(['Suspend! [A2]', 'Loading...', 'Error! [B2]', 'Oops!']); // Remain on previous screen. expect(root).toMatchRenderedOutput('A1B1'); @@ -299,16 +281,7 @@ describe('ReactConcurrentErrorRecovery', () => { root.render(); }); }); - assertLog([ - 'Suspend! [A2]', - 'Loading...', - 'Suspend! [B2]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [A2]', 'Loading...', 'Suspend! [B2]', 'Loading...'] - : []), - ]); + assertLog(['Suspend! [A2]', 'Loading...', 'Suspend! [B2]', 'Loading...']); // Because this is a refresh, we don't switch to a fallback expect(root).toMatchRenderedOutput('A1B1'); @@ -364,11 +337,7 @@ describe('ReactConcurrentErrorRecovery', () => { root.render(); }); }); - assertLog([ - 'Suspend! [Async]', - - ...(gate('enableSiblingPrerendering') ? ['Suspend! [Async]'] : []), - ]); + assertLog(['Suspend! [Async]']); expect(root).toMatchRenderedOutput(null); // This also works if the suspended component is wrapped with an error @@ -384,11 +353,7 @@ describe('ReactConcurrentErrorRecovery', () => { ); }); }); - assertLog([ - 'Suspend! [Async]', - - ...(gate('enableSiblingPrerendering') ? ['Suspend! [Async]'] : []), - ]); + assertLog(['Suspend! [Async]']); expect(root).toMatchRenderedOutput(null); // Continues rendering once data resolves @@ -445,7 +410,7 @@ describe('ReactConcurrentErrorRecovery', () => { 'Suspend! [Async]', ...(gate('enableSiblingPrerendering') - ? ['Suspend! [Async]', 'Caught an error: Oops!'] + ? ['Caught an error: Oops!'] : []), ]); // The render suspended without committing the error. @@ -468,7 +433,7 @@ describe('ReactConcurrentErrorRecovery', () => { 'Suspend! [Async]', ...(gate('enableSiblingPrerendering') - ? ['Suspend! [Async]', 'Caught an error: Oops!'] + ? ['Caught an error: Oops!'] : []), ]); expect(root).toMatchRenderedOutput(null); diff --git a/packages/react-reconciler/src/__tests__/ReactDeferredValue-test.js b/packages/react-reconciler/src/__tests__/ReactDeferredValue-test.js index bf4ecd02eb3be..b2c38696cc028 100644 --- a/packages/react-reconciler/src/__tests__/ReactDeferredValue-test.js +++ b/packages/react-reconciler/src/__tests__/ReactDeferredValue-test.js @@ -420,8 +420,6 @@ describe('ReactDeferredValue', () => { // The initial value suspended, so we attempt the final value, which // also suspends. 'Suspend! [Final]', - - ...(gate('enableSiblingPrerendering') ? ['Suspend! [Final]'] : []), ]); expect(root).toMatchRenderedOutput(null); @@ -461,8 +459,6 @@ describe('ReactDeferredValue', () => { // The initial value suspended, so we attempt the final value, which // also suspends. 'Suspend! [Final]', - - ...(gate('enableSiblingPrerendering') ? ['Suspend! [Final]'] : []), ]); expect(root).toMatchRenderedOutput(null); @@ -535,8 +531,6 @@ describe('ReactDeferredValue', () => { // The initial value suspended, so we attempt the final value, which // also suspends. 'Suspend! [Final]', - - ...(gate('enableSiblingPrerendering') ? ['Suspend! [Final]'] : []), ]); expect(root).toMatchRenderedOutput(null); @@ -546,8 +540,6 @@ describe('ReactDeferredValue', () => { 'Loading...', // Still waiting for the final value. 'Suspend! [Final]', - - ...(gate('enableSiblingPrerendering') ? ['Suspend! [Final]'] : []), ]); expect(root).toMatchRenderedOutput('Loading...'); @@ -592,8 +584,6 @@ describe('ReactDeferredValue', () => { // boundaries work, where we always prefer to show the innermost // loading state.) 'Suspend! [Content]', - - ...(gate('enableSiblingPrerendering') ? ['Suspend! [Content]'] : []), ]); // Still showing the App preview state because the inner // content suspended. diff --git a/packages/react-reconciler/src/__tests__/ReactExpiration-test.js b/packages/react-reconciler/src/__tests__/ReactExpiration-test.js index 50fc16770916f..aef06288667db 100644 --- a/packages/react-reconciler/src/__tests__/ReactExpiration-test.js +++ b/packages/react-reconciler/src/__tests__/ReactExpiration-test.js @@ -654,11 +654,10 @@ describe('ReactExpiration', () => { }); await waitForAll([ 'Suspend! [A1]', - 'Loading...', - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [A1]', 'B', 'C', 'Loading...'] - : []), + ...(gate('enableSiblingPrerendering') ? ['B', 'C'] : []), + + 'Loading...', ]); // Lots of time elapses before the promise resolves diff --git a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js index 640a3d467e499..e62852f2a3ddc 100644 --- a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js +++ b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js @@ -652,22 +652,14 @@ describe('ReactHooksWithNoopRenderer', () => { React.startTransition(() => { root.render(); }); - await waitForAll([ - 'Suspend!', - - ...(gate('enableSiblingPrerendering') ? ['Suspend!'] : []), - ]); + await waitForAll(['Suspend!']); expect(root).toMatchRenderedOutput(); // Rendering again should suspend again. React.startTransition(() => { root.render(); }); - await waitForAll([ - 'Suspend!', - - ...(gate('enableSiblingPrerendering') ? ['Suspend!'] : []), - ]); + await waitForAll(['Suspend!']); }); it('discards render phase updates if something suspends, but not other updates in the same component', async () => { @@ -717,22 +709,14 @@ describe('ReactHooksWithNoopRenderer', () => { setLabel('B'); }); - await waitForAll([ - 'Suspend!', - - ...(gate('enableSiblingPrerendering') ? ['Suspend!'] : []), - ]); + await waitForAll(['Suspend!']); expect(root).toMatchRenderedOutput(); // Rendering again should suspend again. React.startTransition(() => { root.render(); }); - await waitForAll([ - 'Suspend!', - - ...(gate('enableSiblingPrerendering') ? ['Suspend!'] : []), - ]); + await waitForAll(['Suspend!']); // Flip the signal back to "cancel" the update. However, the update to // label should still proceed. It shouldn't have been dropped. @@ -3511,13 +3495,6 @@ describe('ReactHooksWithNoopRenderer', () => { 'Before... Pending: true', 'Suspend! [After... Pending: false]', 'Loading... Pending: false', - - ...(gate('enableSiblingPrerendering') - ? [ - 'Suspend! [After... Pending: false]', - 'Loading... Pending: false', - ] - : []), ]); expect(ReactNoop).toMatchRenderedOutput( , @@ -3586,17 +3563,7 @@ describe('ReactHooksWithNoopRenderer', () => { await act(async () => { _setText('B'); - await waitForAll([ - 'B', - 'A', - 'B', - 'Suspend! [B]', - 'Loading', - - ...(gate('enableSiblingPrerendering') - ? ['B', 'Suspend! [B]', 'Loading'] - : []), - ]); + await waitForAll(['B', 'A', 'B', 'Suspend! [B]', 'Loading']); await waitForAll([]); expect(ReactNoop).toMatchRenderedOutput( <> diff --git a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js index b6c422749683a..891cf91049833 100644 --- a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js @@ -1488,20 +1488,10 @@ describe('ReactLazy', () => { React.startTransition(() => { root.update(); }); - await waitForAll([ - 'Init B2', - 'Loading...', - - ...(gate('enableSiblingPrerendering') ? ['Loading...'] : []), - ]); + await waitForAll(['Init B2', 'Loading...']); await act(() => resolveFakeImport(ChildB2)); // We need to flush to trigger the second one to load. - assertLog([ - 'Init A2', - 'Loading...', - - ...(gate('enableSiblingPrerendering') ? ['Loading...'] : []), - ]); + assertLog(['Init A2', 'Loading...']); await act(() => resolveFakeImport(ChildA2)); assertLog(['b', 'a', 'Did update: b', 'Did update: a']); expect(root).toMatchRenderedOutput('ba'); diff --git a/packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js b/packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js index 164d3a9599eee..76b4ead3c4186 100644 --- a/packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js @@ -135,11 +135,10 @@ describe('ReactSuspense', () => { 'Bar', // A suspends 'Suspend! [A]', - 'Loading...', - ...(gate('enableSiblingPrerendering') - ? ['Foo', 'Bar', 'Suspend! [A]', 'B', 'Loading...'] - : []), + ...(gate('enableSiblingPrerendering') ? ['B'] : []), + + 'Loading...', ]); expect(container.textContent).toEqual(''); diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js index f41de1c169d3c..80e04733bfe35 100644 --- a/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js +++ b/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js @@ -295,22 +295,12 @@ describe('ReactSuspenseWithNoopRenderer', () => { 'Bar', // A suspends 'Suspend! [A]', + + ...(gate('enableSiblingPrerendering') ? ['B'] : []), + // We immediately unwind and switch to a fallback without // rendering siblings. 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? [ - 'Foo', - 'Bar', - // A suspends - 'Suspend! [A]', - 'B', - // We immediately unwind and switch to a fallback without - // rendering siblings. - 'Loading...', - ] - : []), ]); expect(ReactNoop).toMatchRenderedOutput(null); @@ -395,11 +385,10 @@ describe('ReactSuspenseWithNoopRenderer', () => { await waitForAll([ 'A', 'Suspend! [B]', - 'Loading...', - ...(gate('enableSiblingPrerendering') - ? ['A', 'Suspend! [B]', 'C', 'D', 'Loading...'] - : []), + ...(gate('enableSiblingPrerendering') ? ['C', 'D'] : []), + + 'Loading...', ]); // Did not commit yet. expect(ReactNoop).toMatchRenderedOutput(null); @@ -457,14 +446,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { React.startTransition(() => { ReactNoop.render(); }); - await waitForAll([ - 'Suspend! [Result]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [Result]', 'Loading...'] - : []), - ]); + await waitForAll(['Suspend! [Result]', 'Loading...']); expect(ReactNoop).toMatchRenderedOutput(null); await rejectText('Result', new Error('Failed to load: Result')); @@ -579,14 +561,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { // Update the low-pri text await act(() => startTransition(() => setLowPri('2'))); // Suspends - assertLog([ - 'Suspend! [2]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [2]', 'Loading...'] - : []), - ]); + assertLog(['Suspend! [2]', 'Loading...']); // While we're still waiting for the low-pri update to complete, update the // high-pri text at high priority. @@ -631,14 +606,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { React.startTransition(() => { ReactNoop.render(); }); - await waitForAll([ - 'Suspend! [A]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [A]', 'Loading...'] - : []), - ]); + await waitForAll(['Suspend! [A]', 'Loading...']); expect(ReactNoop).toMatchRenderedOutput(null); React.startTransition(() => { @@ -646,11 +614,10 @@ describe('ReactSuspenseWithNoopRenderer', () => { }); await waitForAll([ 'Suspend! [A]', - 'Loading...', - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [A]', 'B', 'Loading...'] - : []), + ...(gate('enableSiblingPrerendering') ? ['B'] : []), + + 'Loading...', ]); expect(ReactNoop).toMatchRenderedOutput(null); @@ -989,14 +956,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { , ); }); - await waitForAll([ - 'Suspend! [Async]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [Async]', 'Loading...'] - : []), - ]); + await waitForAll(['Suspend! [Async]', 'Loading...']); expect(ReactNoop).toMatchRenderedOutput(null); // Resolve the promise @@ -1028,14 +988,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { // Schedule an update, and suspend for up to 5 seconds. React.startTransition(() => ReactNoop.render()); // The update should suspend. - await waitForAll([ - 'Suspend! [A]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [A]', 'Loading...'] - : []), - ]); + await waitForAll(['Suspend! [A]', 'Loading...']); expect(ReactNoop).toMatchRenderedOutput(); // Advance time until right before it expires. @@ -1047,14 +1000,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { // Schedule another low priority update. React.startTransition(() => ReactNoop.render()); // This update should also suspend. - await waitForAll([ - 'Suspend! [B]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [B]', 'Loading...'] - : []), - ]); + await waitForAll(['Suspend! [B]', 'Loading...']); expect(ReactNoop).toMatchRenderedOutput(); // Schedule a regular update. Its expiration time will fall between @@ -2320,16 +2266,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { ReactNoop.render(); }); - await waitForAll([ - 'Foo', - 'A', - 'Suspend! [B]', - 'Loading B...', - - ...(gate('enableSiblingPrerendering') - ? ['Foo', 'A', 'Suspend! [B]', 'Loading B...'] - : []), - ]); + await waitForAll(['Foo', 'A', 'Suspend! [B]', 'Loading B...']); // Transitions never fall back. expect(ReactNoop).toMatchRenderedOutput(); @@ -2405,14 +2342,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { // Start transition. React.startTransition(() => ReactNoop.render()); - await waitForAll([ - 'Suspend! [B]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [B]', 'Loading...'] - : []), - ]); + await waitForAll(['Suspend! [B]', 'Loading...']); Scheduler.unstable_advanceTime(100000); await advanceTimers(100000); // Even after lots of time has passed, we have still not yet flushed the @@ -2463,14 +2393,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { await act(async () => { React.startTransition(() => transitionToPage('B')); - await waitForAll([ - 'Suspend! [B]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [B]', 'Loading...'] - : []), - ]); + await waitForAll(['Suspend! [B]', 'Loading...']); Scheduler.unstable_advanceTime(100000); await advanceTimers(100000); // Even after lots of time has passed, we have still not yet flushed the @@ -2525,14 +2448,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { await act(async () => { React.startTransition(() => transitionToPage('B')); - await waitForAll([ - 'Suspend! [B]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [B]', 'Loading...'] - : []), - ]); + await waitForAll(['Suspend! [B]', 'Loading...']); Scheduler.unstable_advanceTime(100000); await advanceTimers(100000); // Even after lots of time has passed, we have still not yet flushed the @@ -2574,14 +2490,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { // Start transition. React.startTransition(() => ReactNoop.render()); - await waitForAll([ - 'Suspend! [B]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [B]', 'Loading...'] - : []), - ]); + await waitForAll(['Suspend! [B]', 'Loading...']); Scheduler.unstable_advanceTime(2999); await advanceTimers(2999); // Since the timeout is infinite (or effectively infinite), @@ -2595,14 +2504,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { // Start a long (infinite) transition. React.startTransition(() => ReactNoop.render()); - await waitForAll([ - 'Suspend! [C]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [C]', 'Loading...'] - : []), - ]); + await waitForAll(['Suspend! [C]', 'Loading...']); // Even after lots of time has passed, we have still not yet flushed the // loading state. @@ -2650,14 +2552,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { await act(async () => { React.startTransition(() => transitionToPage('B')); - await waitForAll([ - 'Suspend! [B]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [B]', 'Loading...'] - : []), - ]); + await waitForAll(['Suspend! [B]', 'Loading...']); Scheduler.unstable_advanceTime(2999); await advanceTimers(2999); @@ -2675,14 +2570,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { await act(async () => { React.startTransition(() => transitionToPage('C')); - await waitForAll([ - 'Suspend! [C]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [C]', 'Loading...'] - : []), - ]); + await waitForAll(['Suspend! [C]', 'Loading...']); // Even after lots of time has passed, we have still not yet flushed the // loading state. @@ -2734,14 +2622,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { await act(async () => { React.startTransition(() => transitionToPage('B')); - await waitForAll([ - 'Suspend! [B]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [B]', 'Loading...'] - : []), - ]); + await waitForAll(['Suspend! [B]', 'Loading...']); Scheduler.unstable_advanceTime(2999); await advanceTimers(2999); // Since the timeout is infinite (or effectively infinite), @@ -2758,14 +2639,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { await act(async () => { React.startTransition(() => transitionToPage('C')); - await waitForAll([ - 'Suspend! [C]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [C]', 'Loading...'] - : []), - ]); + await waitForAll(['Suspend! [C]', 'Loading...']); // Even after lots of time has passed, we have still not yet flushed the // loading state. @@ -2806,15 +2680,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { // Start transition. React.startTransition(() => ReactNoop.render()); - await waitForAll([ - 'Hi!', - 'Suspend! [B]', - 'Loading B...', - - ...(gate('enableSiblingPrerendering') - ? ['Hi!', 'Suspend! [B]', 'Loading B...'] - : []), - ]); + await waitForAll(['Hi!', 'Suspend! [B]', 'Loading B...']); // Suspended expect(ReactNoop).toMatchRenderedOutput( @@ -2875,15 +2741,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { // Start transition. React.startTransition(() => ReactNoop.render()); - await waitForAll([ - 'Hi!', - 'Suspend! [B]', - 'Loading B...', - - ...(gate('enableSiblingPrerendering') - ? ['Hi!', 'Suspend! [B]', 'Loading B...'] - : []), - ]); + await waitForAll(['Hi!', 'Suspend! [B]', 'Loading B...']); // Suspended expect(ReactNoop).toMatchRenderedOutput( @@ -2997,14 +2855,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { ReactNoop.render(); }); - await waitForAll([ - 'Suspend! [A]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [A]', 'Loading...'] - : []), - ]); + await waitForAll(['Suspend! [A]', 'Loading...']); await resolveText('A'); await waitFor(['A', 'Commit']); expect(ReactNoop).toMatchRenderedOutput( @@ -3056,14 +2907,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { ReactNoop.render(); }); - await waitForAll([ - 'Suspend! [A]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [A]', 'Loading...'] - : []), - ]); + await waitForAll(['Suspend! [A]', 'Loading...']); await resolveText('A'); await waitFor(['A', 'Commit']); expect(ReactNoop).toMatchRenderedOutput( @@ -3842,16 +3686,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { // Update to "a". That will suspend. await act(async () => { setTextWithShortTransition('a'); - await waitForAll([ - 'Pending...', - '', - 'Suspend! [a]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [a]', 'Loading...'] - : []), - ]); + await waitForAll(['Pending...', '', 'Suspend! [a]', 'Loading...']); }); assertLog([]); expect(root).toMatchRenderedOutput( @@ -3870,10 +3705,6 @@ describe('ReactSuspenseWithNoopRenderer', () => { '', 'Suspend! [b]', 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [b]', 'Loading...'] - : []), ]); }); assertLog([]); @@ -3888,14 +3719,7 @@ describe('ReactSuspenseWithNoopRenderer', () => { await act(async () => { await resolveText('a'); - await waitForAll([ - 'Suspend! [b]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [b]', 'Loading...'] - : []), - ]); + await waitForAll(['Suspend! [b]', 'Loading...']); expect(root).toMatchRenderedOutput( <> diff --git a/packages/react-reconciler/src/__tests__/ReactTransition-test.js b/packages/react-reconciler/src/__tests__/ReactTransition-test.js index a0c4ea8db0aff..60456ee6392d2 100644 --- a/packages/react-reconciler/src/__tests__/ReactTransition-test.js +++ b/packages/react-reconciler/src/__tests__/ReactTransition-test.js @@ -199,10 +199,6 @@ describe('ReactTransition', () => { '(empty)', 'Suspend! [Async]', 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [Async]', 'Loading...'] - : []), ]); expect(root).toMatchRenderedOutput('Pending...(empty)'); @@ -273,10 +269,6 @@ describe('ReactTransition', () => { 'B label', 'Suspend! [B content]', 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['B label', 'Suspend! [B content]', 'Loading...'] - : []), ]); // This is a refresh transition so it shouldn't show a fallback expect(root).toMatchRenderedOutput( @@ -298,10 +290,6 @@ describe('ReactTransition', () => { 'C label', 'Suspend! [C content]', 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['C label', 'Suspend! [C content]', 'Loading...'] - : []), ]); expect(root).toMatchRenderedOutput( <> @@ -319,10 +307,6 @@ describe('ReactTransition', () => { 'C label', 'Suspend! [C content]', 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['C label', 'Suspend! [C content]', 'Loading...'] - : []), ]); expect(root).toMatchRenderedOutput( <> @@ -410,10 +394,6 @@ describe('ReactTransition', () => { 'B label', 'Suspend! [B content]', 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['B label', 'Suspend! [B content]', 'Loading...'] - : []), ]); // This is a refresh transition so it shouldn't show a fallback expect(root).toMatchRenderedOutput( @@ -435,10 +415,6 @@ describe('ReactTransition', () => { 'C label', 'Suspend! [C content]', 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['C label', 'Suspend! [C content]', 'Loading...'] - : []), ]); expect(root).toMatchRenderedOutput( <> @@ -456,10 +432,6 @@ describe('ReactTransition', () => { 'C label', 'Suspend! [C content]', 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['C label', 'Suspend! [C content]', 'Loading...'] - : []), ]); expect(root).toMatchRenderedOutput( <> @@ -528,14 +500,7 @@ describe('ReactTransition', () => { setShowA(true); }); }); - assertLog([ - 'Suspend! [A]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [A]', 'Loading...'] - : []), - ]); + assertLog(['Suspend! [A]', 'Loading...']); expect(root).toMatchRenderedOutput(null); // Before A loads, switch to B. This should entangle A with B. @@ -545,14 +510,7 @@ describe('ReactTransition', () => { setShowB(true); }); }); - assertLog([ - 'Suspend! [B]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [B]', 'Loading...'] - : []), - ]); + assertLog(['Suspend! [B]', 'Loading...']); expect(root).toMatchRenderedOutput(null); // Before A or B loads, switch to C. This should entangle C with B, and @@ -563,14 +521,7 @@ describe('ReactTransition', () => { setShowC(true); }); }); - assertLog([ - 'Suspend! [C]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [C]', 'Loading...'] - : []), - ]); + assertLog(['Suspend! [C]', 'Loading...']); expect(root).toMatchRenderedOutput(null); // Now the data starts resolving out of order. @@ -582,14 +533,7 @@ describe('ReactTransition', () => { resolveText('B'); }); }); - assertLog([ - 'Suspend! [C]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [C]', 'Loading...'] - : []), - ]); + assertLog(['Suspend! [C]', 'Loading...']); expect(root).toMatchRenderedOutput(null); // Now resolve A. Again, this will attempt to render C, since everything @@ -599,14 +543,7 @@ describe('ReactTransition', () => { resolveText('A'); }); }); - assertLog([ - 'Suspend! [C]', - 'Loading...', - - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [C]', 'Loading...'] - : []), - ]); + assertLog(['Suspend! [C]', 'Loading...']); expect(root).toMatchRenderedOutput(null); // Finally, resolve C. This time we can finish. @@ -923,11 +860,10 @@ describe('ReactTransition', () => { assertLog([ // Suspend. 'Suspend! [Async]', - 'Loading...', - ...(gate('enableSiblingPrerendering') - ? ['Suspend! [Async]', 'Normal pri: 0', 'Loading...'] - : []), + ...(gate('enableSiblingPrerendering') ? ['Normal pri: 0'] : []), + + 'Loading...', ]); expect(root).toMatchRenderedOutput('(empty), Normal pri: 0'); diff --git a/packages/react-reconciler/src/__tests__/ReactUse-test.js b/packages/react-reconciler/src/__tests__/ReactUse-test.js index c562d43dc3bb8..599b50861fe11 100644 --- a/packages/react-reconciler/src/__tests__/ReactUse-test.js +++ b/packages/react-reconciler/src/__tests__/ReactUse-test.js @@ -1885,10 +1885,6 @@ describe('ReactUse', () => { 'A component was suspended by an uncached promise. Creating ' + 'promises inside a Client Component or hook is not yet ' + 'supported, except via a Suspense-compatible library or framework.', - - ...(gate('enableSiblingPrerendering') - ? ['A component was suspended by an uncached promise.'] - : []), ]); assertLog(['Async text requested [World]']); @@ -1940,10 +1936,6 @@ describe('ReactUse', () => { 'A component was suspended by an uncached promise. Creating ' + 'promises inside a Client Component or hook is not yet ' + 'supported, except via a Suspense-compatible library or framework.', - - ...(gate('enableSiblingPrerendering') - ? ['A component was suspended by an uncached promise.'] - : []), ]); assertLog(['Async text requested [World]']); diff --git a/packages/react-reconciler/src/__tests__/useMemoCache-test.js b/packages/react-reconciler/src/__tests__/useMemoCache-test.js index 8a9b0da68d483..a3dbeeb38ce1c 100644 --- a/packages/react-reconciler/src/__tests__/useMemoCache-test.js +++ b/packages/react-reconciler/src/__tests__/useMemoCache-test.js @@ -559,26 +559,13 @@ describe('useMemoCache()', () => { root.render(); }); }); - assertLog([ - 'Suspend! [chunkA]', - - ...(gate('enableSiblingPrerendering') ? ['Suspend! [chunkA]'] : []), - ]); + assertLog(['Suspend! [chunkA]']); // The data starts to stream in. Loading the data in the first chunk // triggers an expensive computation in the UI. Later, we'll test whether // this computation is reused. await act(() => updatedChunkA.resolve('A2')); - assertLog([ - 'Some expensive processing... [A2]', - 'Suspend! [chunkB]', - - ...(gate('enableSiblingPrerendering') - ? gate('enableNoCloningMemoCache') - ? ['Suspend! [chunkB]'] - : ['Some expensive processing... [A2]', 'Suspend! [chunkB]'] - : []), - ]); + assertLog(['Some expensive processing... [A2]', 'Suspend! [chunkB]']); // The second chunk hasn't loaded yet, so we're still showing the // initial UI. @@ -599,22 +586,11 @@ describe('useMemoCache()', () => { if (gate(flags => flags.enableNoCloningMemoCache)) { // We did not have process the first chunk again. We reused the // computation from the earlier attempt. - assertLog([ - 'Suspend! [chunkB]', - - ...(gate('enableSiblingPrerendering') ? ['Suspend! [chunkB]'] : []), - ]); + assertLog(['Suspend! [chunkB]']); } else { // Because we clone/reset the memo cache after every aborted attempt, we // must process the first chunk again. - assertLog([ - 'Some expensive processing... [A2]', - 'Suspend! [chunkB]', - - ...(gate('enableSiblingPrerendering') - ? ['Some expensive processing... [A2]', 'Suspend! [chunkB]'] - : []), - ]); + assertLog(['Some expensive processing... [A2]', 'Suspend! [chunkB]']); } expect(root).toMatchRenderedOutput( diff --git a/packages/react-reconciler/src/__tests__/useSyncExternalStore-test.js b/packages/react-reconciler/src/__tests__/useSyncExternalStore-test.js index 17e5fe26c0e89..a61b5e49bb890 100644 --- a/packages/react-reconciler/src/__tests__/useSyncExternalStore-test.js +++ b/packages/react-reconciler/src/__tests__/useSyncExternalStore-test.js @@ -275,9 +275,7 @@ describe('useSyncExternalStore', () => { // this test, this causes A to suspend. 'Suspend A', - ...(gate('enableSiblingPrerendering') - ? ['Suspend A', 'B: Updated'] - : []), + ...(gate('enableSiblingPrerendering') ? ['B: Updated'] : []), ]); // Nothing has committed, because A suspended and no fallback // was provided.