Skip to content

Commit fb8e2d7

Browse files
committed
The prior attempt didn't quite work because resume requests don't follow the same logic that the root tasks will be zero after the preamble has been sent.
I decided to restructure things to track whether there was a shell on the resumable state. I also took the opportunity to collapse the hasBody and hasHTML booleans into a bitfield which also tracks the shell. This is probably the right long term layering too because the fact that hoistables can't be written before the preamble is really a dom limitation not a fizz limitation. Now flushes can attempt to write hoistables every time but for dom specifically these will be noops until there is a shell. We have to set whether there is a shell in two places. first we set it during the preamble write. This covers cases like regular renders. we can't write hoistables until we've flushed the shell so flipping the bit here is the right spot. But during a prerender we will complete the prerender and return a postponed state before we've ever started flushing. In this case we need to flip the bit during the completion of the postponed state. In either case this produces a runtime where during render hositables always come after the preamble and during resumes hoistables only come after the preamble if the prenreder did not cotnain the shell.
1 parent ea441f6 commit fb8e2d7

2 files changed

Lines changed: 25 additions & 16 deletions

File tree

packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ const SentClientRenderFunction /* */ = 0b00100;
119119
const SentStyleInsertionFunction /* */ = 0b01000;
120120
const SentFormReplayingRuntime /* */ = 0b10000;
121121

122+
type Parts = number;
123+
const NoParts /* */ = 0b00000;
124+
const HasShell /* */ = 0b00001;
125+
const HasHTML /* */ = 0b00010;
126+
const HasBody /* */ = 0b00100;
127+
122128
// Per request, global state that is not contextual to the rendering subtree.
123129
// This cannot be resumed and therefore should only contain things that are
124130
// temporary working state or are never used in the prerender pass.
@@ -245,9 +251,8 @@ export type ResumableState = {
245251
// state for script streaming format, unused if using external runtime / data
246252
instructions: InstructionState,
247253

248-
// postamble state
249-
hasBody: boolean,
250-
hasHtml: boolean,
254+
// preamble & postamble state
255+
parts: Parts,
251256

252257
// Resources - Request local cache
253258
unknownResources: {
@@ -637,8 +642,7 @@ export function createResumableState(
637642
bootstrapScripts,
638643
bootstrapModules,
639644
instructions: NothingSent,
640-
hasBody: false,
641-
hasHtml: false,
645+
parts: NoParts,
642646

643647
// @TODO add bootstrap script to implicit preloads
644648

@@ -665,8 +669,7 @@ export function resetResumableState(
665669
// Resets the resumable state based on what didn't manage to fully flush in the render state.
666670
// This currently assumes nothing was flushed.
667671
resumableState.nextFormID = 0;
668-
resumableState.hasBody = false;
669-
resumableState.hasHtml = false;
672+
resumableState.parts = NoParts;
670673
resumableState.unknownResources = {
671674
font: renderState.resets.font,
672675
};
@@ -681,6 +684,7 @@ export function resetResumableState(
681684

682685
export function completeResumableState(resumableState: ResumableState): void {
683686
// This function is called when we have completed a prerender and there is a shell.
687+
resumableState.parts |= HasShell;
684688
resumableState.bootstrapScriptContent = undefined;
685689
resumableState.bootstrapScripts = undefined;
686690
resumableState.bootstrapModules = undefined;
@@ -3708,14 +3712,14 @@ export function pushEndInstance(
37083712
// we won't emit any more tags
37093713
case 'body': {
37103714
if (formatContext.insertionMode <= HTML_HTML_MODE) {
3711-
resumableState.hasBody = true;
3715+
resumableState.parts |= HasBody;
37123716
return;
37133717
}
37143718
break;
37153719
}
37163720
case 'html':
37173721
if (formatContext.insertionMode === ROOT_HTML_MODE) {
3718-
resumableState.hasHtml = true;
3722+
resumableState.parts |= HasHTML;
37193723
return;
37203724
}
37213725
break;
@@ -4602,6 +4606,8 @@ export function writePreamble(
46024606
renderState: RenderState,
46034607
willFlushAllSegments: boolean,
46044608
): void {
4609+
resumableState.parts |= HasShell;
4610+
46054611
// This function must be called exactly once on every request
46064612
if (
46074613
enableFizzExternalRuntime &&
@@ -4707,6 +4713,11 @@ export function writeHoistables(
47074713
resumableState: ResumableState,
47084714
renderState: RenderState,
47094715
): void {
4716+
if ((resumableState.parts & HasShell) === NoParts) {
4717+
// We can't write anything until we've written the shell.
4718+
return;
4719+
}
4720+
47104721
let i = 0;
47114722

47124723
// Emit high priority Hoistables
@@ -4759,10 +4770,10 @@ export function writePostamble(
47594770
destination: Destination,
47604771
resumableState: ResumableState,
47614772
): void {
4762-
if (resumableState.hasBody) {
4773+
if (resumableState.parts & HasBody) {
47634774
writeChunk(destination, endChunkForTag('body'));
47644775
}
4765-
if (resumableState.hasHtml) {
4776+
if (resumableState.parts & HasHTML) {
47664777
writeChunk(destination, endChunkForTag('html'));
47674778
}
47684779
}

packages/react-server/src/ReactFizzServer.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4057,17 +4057,15 @@ function flushCompletedQueues(
40574057
// that item fully and then yield. At that point we remove the already completed
40584058
// items up until the point we completed them.
40594059

4060-
if (request.pendingRootTasks > 0) {
4061-
// When there are pending root tasks we don't want to flush anything
4062-
return;
4063-
}
4064-
40654060
let i;
40664061
const completedRootSegment = request.completedRootSegment;
40674062
if (completedRootSegment !== null) {
40684063
if (completedRootSegment.status === POSTPONED) {
40694064
// We postponed the root, so we write nothing.
40704065
return;
4066+
} else if (request.pendingRootTasks > 0) {
4067+
// The root is blocked on additional tasks.
4068+
return;
40714069
}
40724070

40734071
flushPreamble(request, destination, completedRootSegment);

0 commit comments

Comments
 (0)