Skip to content

Commit

Permalink
Fix: change the timing for clearing the inlined data buffer (#35413)
Browse files Browse the repository at this point in the history
Fix error of `Unexpected server data: missing bootstrap script` when the initial rendering is streaming rsc.

Introduced in #35344, the buffer might still keep flushing after on mount.


Co-authored-by: Shu Ding <3676859+shuding@users.noreply.github.com>
  • Loading branch information
huozhi and shuding committed Mar 18, 2022
1 parent 279f47b commit f2fee3d
Showing 1 changed file with 34 additions and 12 deletions.
46 changes: 34 additions & 12 deletions packages/next/client/index.tsx
Expand Up @@ -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 = []
Expand All @@ -695,24 +699,45 @@ 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) {
initialServerDataWriter.close()
}
},
false
)
const DOMContentLoaded = function () {
if (initialServerDataWriter && !initialServerDataFlushed) {
initialServerDataWriter.close()
initialServerDataFlushed = true
initialServerDataBuffer = undefined
}
initialServerDataLoaded = true
}
// It's possible that the DOM is already loaded.
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', DOMContentLoaded, false)
} else {
DOMContentLoaded()
}

const nextServerDataLoadingGlobal = ((self as any).__next_s =
(self as any).__next_s || [])
Expand Down Expand Up @@ -771,9 +796,6 @@ if (process.env.__NEXT_RSC) {
React.useEffect(() => {
rscCache.delete(cacheKey)
})
React.useEffect(() => {
initialServerDataBuffer = undefined
}, [])
const response = useServerResponse(cacheKey, serialized)
const root = response.readRoot()
return root
Expand Down

0 comments on commit f2fee3d

Please sign in to comment.