diff --git a/packages/next/client/index.tsx b/packages/next/client/index.tsx index 019bee1f7513..9dd60546f861 100644 --- a/packages/next/client/index.tsx +++ b/packages/next/client/index.tsx @@ -678,9 +678,13 @@ if (process.env.__NEXT_RSC) { } = require('next/dist/compiled/react-server-dom-webpack') const encoder = new TextEncoder() + let initialServerDataBuffer: string[] | undefined = undefined let initialServerDataWriter: WritableStreamDefaultWriter | undefined = undefined + let initialServerDataLoaded = false + let initialServerDataFlushed = false + function nextServerDataCallback(seg: [number, string, string]) { if (seg[0] === 0) { initialServerDataBuffer = [] @@ -695,22 +699,40 @@ if (process.env.__NEXT_RSC) { } } } + + // There might be race conditions between `nextServerDataRegisterWriter` and + // `DOMContentLoaded`. The former will be called when React starts to hydrate + // the root, the latter will be called when the DOM is fully loaded. + // For streaming, the former is called first due to partial hydration. + // For non-streaming, the latter can be called first. + // Hence, we use two variables `initialServerDataLoaded` and + // `initialServerDataFlushed` to make sure the writer will be closed and + // `initialServerDataBuffer` will be cleared in the right time. function nextServerDataRegisterWriter(writer: WritableStreamDefaultWriter) { if (initialServerDataBuffer) { initialServerDataBuffer.forEach((val) => { writer.write(encoder.encode(val)) }) + if (initialServerDataLoaded && !initialServerDataFlushed) { + writer.close() + initialServerDataFlushed = true + initialServerDataBuffer = undefined + } } + initialServerDataWriter = writer } + // When `DOMContentLoaded`, we can close all pending writers to finish hydration. document.addEventListener( 'DOMContentLoaded', function () { - if (initialServerDataWriter && !initialServerDataWriter.closed) { + if (initialServerDataWriter && !initialServerDataFlushed) { initialServerDataWriter.close() + initialServerDataFlushed = true + initialServerDataBuffer = undefined } - initialServerDataBuffer = undefined + initialServerDataLoaded = true }, false )