diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index 40eed7a71325a..3190cd0035b33 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -43,6 +43,7 @@ import { disableLegacyMode, disableDefaultPropsExceptForClasses, disableStringRefs, + enableSiblingPrerendering, } from 'shared/ReactFeatureFlags'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import is from 'shared/objectIs'; @@ -1700,6 +1701,10 @@ function handleThrow(root: FiberRoot, thrownValue: any): void { // deprecate the old API in favor of `use`. thrownValue = getSuspendedThenable(); workInProgressSuspendedReason = + // TODO: Suspending the work loop during the render phase is + // currently not compatible with sibling prerendering. We will add + // this optimization back in a later step. + !enableSiblingPrerendering && shouldRemainOnPreviousScreen() && // Check if there are other pending updates that might possibly unblock this // component from suspending. This mirrors the check in diff --git a/packages/react-reconciler/src/__tests__/ReactUse-test.js b/packages/react-reconciler/src/__tests__/ReactUse-test.js index 1dae8a56dd25f..0abb47d516a3d 100644 --- a/packages/react-reconciler/src/__tests__/ReactUse-test.js +++ b/packages/react-reconciler/src/__tests__/ReactUse-test.js @@ -558,6 +558,7 @@ describe('ReactUse', () => { } }); + // @gate enableSuspendingDuringWorkLoop it('during a transition, can unwrap async operations even if nothing is cached', async () => { function App() { return ; @@ -593,6 +594,7 @@ describe('ReactUse', () => { expect(root).toMatchRenderedOutput('Async'); }); + // @gate enableSuspendingDuringWorkLoop it("does not prevent a Suspense fallback from showing if it's a new boundary, even during a transition", async () => { function App() { return ; @@ -635,6 +637,7 @@ describe('ReactUse', () => { expect(root).toMatchRenderedOutput('Async'); }); + // @gate enableSuspendingDuringWorkLoop it('when waiting for data to resolve, a fresh update will trigger a restart', async () => { function App() { return ; @@ -666,6 +669,7 @@ describe('ReactUse', () => { assertLog(['Something different']); }); + // @gate enableSuspendingDuringWorkLoop it('when waiting for data to resolve, an update on a different root does not cause work to be dropped', async () => { const promise = getAsyncText('Hi'); @@ -708,6 +712,7 @@ describe('ReactUse', () => { expect(root1).toMatchRenderedOutput('Hi'); }); + // @gate enableSuspendingDuringWorkLoop it('while suspended, hooks cannot be called (i.e. current dispatcher is unset correctly)', async () => { function App() { return ; @@ -845,6 +850,7 @@ describe('ReactUse', () => { expect(root).toMatchRenderedOutput('(empty)'); }); + // @gate enableSuspendingDuringWorkLoop it('when replaying a suspended component, reuses the hooks computed during the previous attempt (Memo)', async () => { function ExcitingText({text}) { // This computes the uppercased version of some text. Pretend it's an @@ -894,6 +900,7 @@ describe('ReactUse', () => { ]); }); + // @gate enableSuspendingDuringWorkLoop it('when replaying a suspended component, reuses the hooks computed during the previous attempt (State)', async () => { let _setFruit; let _setVegetable; @@ -950,6 +957,7 @@ describe('ReactUse', () => { expect(root).toMatchRenderedOutput('banana dill'); }); + // @gate enableSuspendingDuringWorkLoop it('when replaying a suspended component, reuses the hooks computed during the previous attempt (DebugValue+State)', async () => { // Make sure we don't get a Hook mismatch warning on updates if there were non-stateful Hooks before the use(). let _setLawyer; @@ -991,6 +999,7 @@ describe('ReactUse', () => { expect(root).toMatchRenderedOutput('aguacate avocat'); }); + // @gate enableSuspendingDuringWorkLoop it( 'wrap an async function with useMemo to skip running the function ' + 'twice when loading new data', @@ -1073,6 +1082,7 @@ describe('ReactUse', () => { expect(root).toMatchRenderedOutput('ABC'); }); + // @gate enableSuspendingDuringWorkLoop it('load multiple nested Suspense boundaries (uncached requests)', async () => { // This the same as the previous test, except the requests are not cached. // The tree should still eventually resolve, despite the @@ -1196,6 +1206,7 @@ describe('ReactUse', () => { expect(root).toMatchRenderedOutput('Hi'); }); + // @gate enableSuspendingDuringWorkLoop it('basic async component', async () => { async function App() { await getAsyncText('Hi'); @@ -1220,6 +1231,7 @@ describe('ReactUse', () => { expect(root).toMatchRenderedOutput('Hi'); }); + // @gate enableSuspendingDuringWorkLoop it('async child of a non-function component (e.g. a class)', async () => { class App extends React.Component { async render() { diff --git a/scripts/jest/TestFlags.js b/scripts/jest/TestFlags.js index 6ced58a32be3c..b970955491aae 100644 --- a/scripts/jest/TestFlags.js +++ b/scripts/jest/TestFlags.js @@ -83,6 +83,10 @@ function getTestFlags() { enableActivity: releaseChannel === 'experimental' || www || xplat, enableSuspenseList: releaseChannel === 'experimental' || www || xplat, enableLegacyHidden: www, + // TODO: Suspending the work loop during the render phase is currently + // not compatible with sibling prerendering. We will add this optimization + // back in a later step. + enableSuspendingDuringWorkLoop: !featureFlags.enableSiblingPrerendering, // This flag is used to determine whether we should run Fizz tests using // the external runtime or the inline script runtime.