diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index e16b2189b37f6..9d308c8e0acec 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -6131,4 +6131,55 @@ describe('ReactDOMFizzServer', () => { // However, it does error the shell. expect(fatalErrors).toEqual(['testing postpone']); }); + + it( + 'a transition that flows into a dehydrated boundary should not suspend ' + + 'if the boundary is showing a fallback', + async () => { + let setSearch; + function App() { + const [search, _setSearch] = React.useState('initial query'); + setSearch = _setSearch; + return ( +
+
{search}
+
+ + + +
+
+ ); + } + + // Render the initial HTML, which is showing a fallback. + await act(() => { + const {pipe} = renderToPipeableStream(); + pipe(writable); + }); + + // Start hydrating. + await clientAct(() => { + ReactDOMClient.hydrateRoot(container, ); + }); + expect(getVisibleChildren(container)).toEqual( +
+
initial query
+
Loading...
+
, + ); + + // Before the HTML has streamed in, update the query. The part outside + // the fallback should be allowed to finish. + await clientAct(() => { + React.startTransition(() => setSearch('updated query')); + }); + expect(getVisibleChildren(container)).toEqual( +
+
updated query
+
Loading...
+
, + ); + }, + ); }); diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index 57a86e6984d80..abb4d54373560 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -2958,7 +2958,15 @@ function updateDehydratedSuspenseComponent( // TODO: We should ideally have a sync hydration lane that we can apply to do // a pass where we hydrate this subtree in place using the previous Context and then // reapply the update afterwards. - renderDidSuspendDelayIfPossible(); + if (isSuspenseInstancePending(suspenseInstance)) { + // This is a dehydrated suspense instance. We don't need to suspend + // because we're already showing a fallback. + // TODO: The Fizz runtime might still stream in completed HTML, out-of- + // band. Should we fix this? There's a version of this bug that happens + // during client rendering, too. Needs more consideration. + } else { + renderDidSuspendDelayIfPossible(); + } return retrySuspenseComponentWithoutHydrating( current, workInProgress,