From ea441f6a6a797026cca80a92c9f96e21108d6f77 Mon Sep 17 00:00:00 2001 From: Josh Story Date: Tue, 9 Apr 2024 14:24:29 -0700 Subject: [PATCH] [Fizz] hoistables should never flush before the preamble Hoistables should never flush before the preamble however there is a surprisingly easy way to trigger this to happen by suspending in the shell of the app. This change modifies the flushing behavior to not emit any hoistables before the preamble has written. It accomplishes this by aborting the flush early if there are any pending root tasks remaining. It's unforunate we need this extra condition but it's essential that we don't emit anything before the preamble and at the moment I don't see a way to do that without introducing a new condition. --- .../src/__tests__/ReactDOMFloat-test.js | 45 +++++++++++++++++++ packages/react-server/src/ReactFizzServer.js | 19 ++++---- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js index c71af838e65dd..0aaaf1a00a71c 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js @@ -5002,6 +5002,51 @@ body { ); }); + it('should never flush hoistables before the preamble', async () => { + let resolve; + const promise = new Promise(res => { + resolve = res; + }); + + function App() { + ReactDOM.preinit('foo', {as: 'script'}); + React.use(promise); + return ( + + hello + + ); + } + + await act(() => { + renderToPipeableStream().pipe(writable); + }); + + // we assert the default JSDOM still in tact + expect(getMeaningfulChildren(document)).toEqual( + + + +
+ + , + ); + + await act(() => { + resolve(); + }); + + // we assert the DOM was replaced entirely because we streamed an opening html tag + expect(getMeaningfulChildren(document)).toEqual( + + +