diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
index df1c71f3fd586..50afd73d1f937 100644
--- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
@@ -6926,7 +6926,14 @@ describe('ReactDOMFizzServer', () => {
expect(getVisibleChildren(container)).toEqual(
{'Loading1'}
- {'Hello'}
+ {/*
+ This used to show "Hello" in this slot because the boundary was able to be flushed
+ early but we now prevent flushing while pendingRootTasks is not zero. This is how Edge
+ would work anyway because you don't get the stream until the root is unblocked on a resume
+ so Node now aligns with edge bevavior
+ {'Hello'}
+ */}
+ {'Loading2'}
{'Loading3'}
,
);
diff --git a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js
index 3eabcb5781c29..b2331c17f2829 100644
--- a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js
@@ -5034,6 +5034,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(
+
+
+
+
+ hello
+ ,
+ );
+ });
+
describe('ReactDOM.prefetchDNS(href)', () => {
it('creates a dns-prefetch resource when called', async () => {
function App({url}) {
diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js
index 223d63a4c1084..7ba6f334e759a 100644
--- a/packages/react-server/src/ReactFizzServer.js
+++ b/packages/react-server/src/ReactFizzServer.js
@@ -4057,22 +4057,25 @@ function flushCompletedQueues(
// that item fully and then yield. At that point we remove the already completed
// items up until the point we completed them.
+ if (request.pendingRootTasks > 0) {
+ // When there are pending root tasks we don't want to flush anything
+ return;
+ }
+
let i;
const completedRootSegment = request.completedRootSegment;
if (completedRootSegment !== null) {
if (completedRootSegment.status === POSTPONED) {
// We postponed the root, so we write nothing.
return;
- } else if (request.pendingRootTasks === 0) {
- flushPreamble(request, destination, completedRootSegment);
- flushSegment(request, destination, completedRootSegment, null);
- request.completedRootSegment = null;
- writeCompletedRoot(destination, request.renderState);
- } else {
- // We haven't flushed the root yet so we don't need to check any other branches further down
- return;
}
+
+ flushPreamble(request, destination, completedRootSegment);
+ flushSegment(request, destination, completedRootSegment, null);
+ request.completedRootSegment = null;
+ writeCompletedRoot(destination, request.renderState);
}
+
writeHoistables(destination, request.resumableState, request.renderState);
// We emit client rendering instructions for already emitted boundaries first.
// This is so that we can signal to the client to start client rendering them as