Skip to content

Commit 6fa5d40

Browse files
committed
switch to embeddign bootstrap scripts in templates to delay hydration
1 parent c184f74 commit 6fa5d40

File tree

10 files changed

+219
-269
lines changed

10 files changed

+219
-269
lines changed

packages/react-dom-bindings/src/client/ReactDOMHostConfig.js

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import type {
1515
IntersectionObserverOptions,
1616
ObserveVisibleRectsCallback,
1717
} from 'react-reconciler/src/ReactTestSelectors';
18-
import type {ReactScopeInstance, Thenable} from 'shared/ReactTypes';
18+
import type {ReactScopeInstance} from 'shared/ReactTypes';
1919
import type {AncestorInfoDev} from './validateDOMNesting';
2020

2121
import {
@@ -1409,24 +1409,6 @@ export function errorHydratingContainer(parentContainer: Container): void {
14091409
}
14101410
}
14111411

1412-
export function getHydrationReadySignal(
1413-
container: Container,
1414-
): null | Thenable<mixed> {
1415-
const signal = (container: any)._r;
1416-
if (
1417-
signal !== null &&
1418-
typeof signal === 'object' &&
1419-
typeof signal.then === 'function'
1420-
) {
1421-
return signal;
1422-
}
1423-
return null;
1424-
}
1425-
1426-
export function clearHydrationReadySignal(container: Container) {
1427-
(container: any)._r = null;
1428-
}
1429-
14301412
// -------------------
14311413
// Test Selectors
14321414
// -------------------

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

Lines changed: 145 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2563,6 +2563,12 @@ const completeBoundaryOrContainerScriptEnd = stringToPrecomputedChunk(
25632563
')</script>',
25642564
);
25652565

2566+
const bootstrapContainerOpenStart = stringToPrecomputedChunk(
2567+
'<template id="bs:',
2568+
);
2569+
const bootstrapContainerOpenEnd = stringToPrecomputedChunk('">');
2570+
const bootstrapContainerClose = stringToPrecomputedChunk('</template>');
2571+
25662572
const completeContainerData1 = stringToPrecomputedChunk(
25672573
'<template data-rci="c" data-bid="',
25682574
);
@@ -2581,7 +2587,11 @@ const completeBoundaryOrContainerData2 = stringToPrecomputedChunk(
25812587
const completeBoundaryOrContainerData3 = stringToPrecomputedChunk(
25822588
'" data-sty="',
25832589
);
2584-
const completeBoundaryOrContainerDataEnd = dataElementQuotedEnd;
2590+
const completeBoundaryOrContainerDataOpenEnd = stringToPrecomputedChunk('">');
2591+
const completeBoundaryOrContainerDataClose = stringToPrecomputedChunk(
2592+
'</template>',
2593+
);
2594+
const completeBoundaryOrContainerDataEmptyEnd = dataElementQuotedEnd;
25852595

25862596
export function writeCompletedBoundaryInstruction(
25872597
destination: Destination,
@@ -2605,97 +2615,184 @@ export function writeCompletedBoundaryInstruction(
26052615
responseState.streamingFormat === ScriptStreamingFormat;
26062616
let r = true;
26072617
if (scriptFormat) {
2608-
writeChunk(destination, responseState.startInlineScript);
26092618
if (enableFloat && hasStyleDependencies) {
2610-
if (!responseState.sentStyleInsertionFunction) {
2611-
responseState.sentStyleInsertionFunction = true;
2612-
writeChunk(destination, clonePrecomputedChunk(styleInsertionFunction));
2613-
}
26142619
if (boundaryID === responseState.containerBoundaryID) {
2620+
// We emit the bootstrap chunks unto a template container with an id
2621+
// formed from the root segment ID. The insertion script will inject
2622+
// the bootstrap scripts into the DOM after revealing the content.
2623+
// We don't do this for cases where there are no style dependencies because
2624+
// the content swap is synchronous and will therefore always complete before
2625+
// the bootstrap scripts run
2626+
const bootstrapChunks = responseState.bootstrapChunks;
2627+
if (bootstrapChunks.length) {
2628+
writeChunk(destination, bootstrapContainerOpenStart);
2629+
writeChunk(destination, responseState.segmentPrefix);
2630+
writeChunk(destination, formattedContentID);
2631+
writeChunk(destination, bootstrapContainerOpenEnd);
2632+
for (let i = 0; i < bootstrapChunks.length; i++) {
2633+
writeChunk(destination, bootstrapChunks[i]);
2634+
}
2635+
writeChunk(destination, bootstrapContainerClose);
2636+
}
2637+
2638+
writeChunk(destination, responseState.startInlineScript);
2639+
if (!responseState.sentStyleInsertionFunction) {
2640+
responseState.sentStyleInsertionFunction = true;
2641+
writeChunk(
2642+
destination,
2643+
clonePrecomputedChunk(styleInsertionFunction),
2644+
);
2645+
}
26152646
if (!responseState.sentCompleteContainerFunction) {
26162647
responseState.sentCompleteContainerFunction = true;
26172648
writeChunk(destination, completeContainerFunction);
26182649
}
26192650
writeChunk(destination, completeContainerWithStylesScript1);
2651+
writeChunk(destination, boundaryID);
2652+
writeChunk(destination, completeBoundaryOrContainerScript2);
2653+
writeChunk(destination, responseState.segmentPrefix);
2654+
writeChunk(destination, formattedContentID);
2655+
writeChunk(destination, completeBoundaryOrContainerScript2a);
2656+
writeStyleResourceDependenciesInJS(destination, boundaryResources);
2657+
return writeChunkAndReturn(
2658+
destination,
2659+
completeBoundaryOrContainerScriptEnd,
2660+
);
26202661
} else {
2662+
writeChunk(destination, responseState.startInlineScript);
2663+
if (!responseState.sentStyleInsertionFunction) {
2664+
responseState.sentStyleInsertionFunction = true;
2665+
writeChunk(
2666+
destination,
2667+
clonePrecomputedChunk(styleInsertionFunction),
2668+
);
2669+
}
26212670
if (!responseState.sentCompleteBoundaryFunction) {
26222671
responseState.sentCompleteBoundaryFunction = true;
26232672
writeChunk(destination, completeBoundaryFunction);
26242673
}
26252674
writeChunk(destination, completeBoundaryWithStylesScript1);
2675+
writeChunk(destination, boundaryID);
2676+
writeChunk(destination, completeBoundaryOrContainerScript2);
2677+
writeChunk(destination, responseState.segmentPrefix);
2678+
writeChunk(destination, formattedContentID);
2679+
writeChunk(destination, completeBoundaryOrContainerScript2a);
2680+
writeStyleResourceDependenciesInJS(destination, boundaryResources);
2681+
return writeChunkAndReturn(
2682+
destination,
2683+
completeBoundaryOrContainerScriptEnd,
2684+
);
26262685
}
2627-
writeChunk(destination, boundaryID);
2628-
writeChunk(destination, completeBoundaryOrContainerScript2);
2629-
writeChunk(destination, responseState.segmentPrefix);
2630-
writeChunk(destination, formattedContentID);
2631-
writeChunk(destination, completeBoundaryOrContainerScript2a);
2632-
writeStyleResourceDependenciesInJS(destination, boundaryResources);
2633-
r = writeChunkAndReturn(
2634-
destination,
2635-
completeBoundaryOrContainerScriptEnd,
2636-
);
26372686
} else {
26382687
if (boundaryID === responseState.containerBoundaryID) {
2688+
writeChunk(destination, responseState.startInlineScript);
26392689
if (!responseState.sentCompleteContainerFunction) {
26402690
responseState.sentCompleteContainerFunction = true;
26412691
writeChunk(destination, completeContainerFunction);
26422692
}
26432693
writeChunk(destination, completeContainerScript1);
2694+
writeChunk(destination, boundaryID);
2695+
writeChunk(destination, completeBoundaryOrContainerScript2);
2696+
writeChunk(destination, responseState.segmentPrefix);
2697+
writeChunk(destination, formattedContentID);
2698+
writeChunk(destination, completeBoundaryOrContainerScript3);
2699+
r = writeChunkAndReturn(
2700+
destination,
2701+
completeBoundaryOrContainerScriptEnd,
2702+
);
2703+
2704+
// We emit bootstrap scripts after the completeContainer instruction
2705+
// because we want to ensure the reveal ocurrs before the bootstrap
2706+
// scripts execute. Notice that unlike with the styles case the scripts
2707+
// are not embedded in a template
2708+
const bootstrapChunks = responseState.bootstrapChunks;
2709+
let i = 0;
2710+
for (; i < bootstrapChunks.length - 1; i++) {
2711+
writeChunk(destination, bootstrapChunks[i]);
2712+
}
2713+
if (i < bootstrapChunks.length) {
2714+
r = writeChunkAndReturn(destination, bootstrapChunks[i]);
2715+
}
2716+
return r;
26442717
} else {
2718+
writeChunk(destination, responseState.startInlineScript);
26452719
if (!responseState.sentCompleteBoundaryFunction) {
26462720
responseState.sentCompleteBoundaryFunction = true;
26472721
writeChunk(destination, completeBoundaryFunction);
26482722
}
26492723
writeChunk(destination, completeBoundaryScript1);
2724+
writeChunk(destination, boundaryID);
2725+
writeChunk(destination, completeBoundaryOrContainerScript2);
2726+
writeChunk(destination, responseState.segmentPrefix);
2727+
writeChunk(destination, formattedContentID);
2728+
writeChunk(destination, completeBoundaryOrContainerScript3);
2729+
return writeChunkAndReturn(
2730+
destination,
2731+
completeBoundaryOrContainerScriptEnd,
2732+
);
26502733
}
2651-
writeChunk(destination, boundaryID);
2652-
writeChunk(destination, completeBoundaryOrContainerScript2);
2653-
writeChunk(destination, responseState.segmentPrefix);
2654-
writeChunk(destination, formattedContentID);
2655-
writeChunk(destination, completeBoundaryOrContainerScript3);
2656-
r = writeChunkAndReturn(
2657-
destination,
2658-
completeBoundaryOrContainerScriptEnd,
2659-
);
26602734
}
26612735
} else {
26622736
if (enableFloat && hasStyleDependencies) {
26632737
if (boundaryID === responseState.containerBoundaryID) {
26642738
writeChunk(destination, completeContainerWithStylesData1);
2739+
writeChunk(destination, boundaryID);
2740+
writeChunk(destination, completeBoundaryOrContainerData2);
2741+
writeChunk(destination, responseState.segmentPrefix);
2742+
writeChunk(destination, formattedContentID);
2743+
writeChunk(destination, completeBoundaryOrContainerData3);
2744+
writeStyleResourceDependenciesInAttr(destination, boundaryResources);
2745+
writeChunk(destination, completeBoundaryOrContainerDataOpenEnd);
2746+
const bootstrapChunks = responseState.bootstrapChunks;
2747+
for (let i = 0; i < bootstrapChunks.length; i++) {
2748+
writeChunk(destination, bootstrapChunks[i]);
2749+
}
2750+
return writeChunkAndReturn(
2751+
destination,
2752+
completeBoundaryOrContainerDataClose,
2753+
);
26652754
} else {
26662755
writeChunk(destination, completeBoundaryWithStylesData1);
2756+
writeChunk(destination, boundaryID);
2757+
writeChunk(destination, completeBoundaryOrContainerData2);
2758+
writeChunk(destination, responseState.segmentPrefix);
2759+
writeChunk(destination, formattedContentID);
2760+
writeChunk(destination, completeBoundaryOrContainerData3);
2761+
writeStyleResourceDependenciesInAttr(destination, boundaryResources);
2762+
return writeChunkAndReturn(
2763+
destination,
2764+
completeBoundaryOrContainerDataEmptyEnd,
2765+
);
26672766
}
2668-
writeChunk(destination, boundaryID);
2669-
writeChunk(destination, completeBoundaryOrContainerData2);
2670-
writeChunk(destination, responseState.segmentPrefix);
2671-
writeChunk(destination, formattedContentID);
2672-
writeChunk(destination, completeBoundaryOrContainerData3);
2673-
writeStyleResourceDependenciesInAttr(destination, boundaryResources);
2674-
r = writeChunkAndReturn(destination, completeBoundaryOrContainerDataEnd);
26752767
} else {
26762768
if (boundaryID === responseState.containerBoundaryID) {
26772769
writeChunk(destination, completeContainerData1);
2770+
writeChunk(destination, boundaryID);
2771+
writeChunk(destination, completeBoundaryOrContainerData2);
2772+
writeChunk(destination, responseState.segmentPrefix);
2773+
writeChunk(destination, formattedContentID);
2774+
writeChunk(destination, completeBoundaryOrContainerDataOpenEnd);
2775+
const bootstrapChunks = responseState.bootstrapChunks;
2776+
for (let i = 0; i < bootstrapChunks.length; i++) {
2777+
writeChunk(destination, bootstrapChunks[i]);
2778+
}
2779+
return writeChunkAndReturn(
2780+
destination,
2781+
completeBoundaryOrContainerDataOpenEnd,
2782+
);
26782783
} else {
26792784
writeChunk(destination, completeBoundaryData1);
2785+
writeChunk(destination, boundaryID);
2786+
writeChunk(destination, completeBoundaryOrContainerData2);
2787+
writeChunk(destination, responseState.segmentPrefix);
2788+
writeChunk(destination, formattedContentID);
2789+
return writeChunkAndReturn(
2790+
destination,
2791+
completeBoundaryOrContainerDataEmptyEnd,
2792+
);
26802793
}
2681-
writeChunk(destination, boundaryID);
2682-
writeChunk(destination, completeBoundaryOrContainerData2);
2683-
writeChunk(destination, responseState.segmentPrefix);
2684-
writeChunk(destination, formattedContentID);
2685-
r = writeChunkAndReturn(destination, completeBoundaryOrContainerDataEnd);
26862794
}
26872795
}
2688-
if (boundaryID === responseState.containerBoundaryID) {
2689-
const bootstrapChunks = responseState.bootstrapChunks;
2690-
let i = 0;
2691-
for (; i < bootstrapChunks.length - 1; i++) {
2692-
writeChunk(destination, bootstrapChunks[i]);
2693-
}
2694-
if (i < bootstrapChunks.length) {
2695-
return writeChunkAndReturn(destination, bootstrapChunks[i]);
2696-
}
2697-
}
2698-
return r;
26992796
}
27002797

27012798
const clientRenderScript1Full = stringToPrecomputedChunk(

packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSet.js

Lines changed: 28 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ export function completeBoundaryWithStyles(
132132
}
133133
}
134134

135-
const allInsertions = Promise.all(dependencies).then(
135+
Promise.all(dependencies).then(
136136
completeBoundaryImpl.bind(null, suspenseBoundaryID, contentID, ''),
137137
completeBoundaryImpl.bind(
138138
null,
@@ -141,13 +141,6 @@ export function completeBoundaryWithStyles(
141141
'Stylesheet failed to load',
142142
),
143143
);
144-
145-
if (isContainer) {
146-
const containerNode = document.getElementById(suspenseBoundaryID);
147-
if (containerNode) {
148-
containerNode['_r'] = allInsertions;
149-
}
150-
}
151144
}
152145

153146
export function completeBoundary(suspenseBoundaryID, contentID, errorDigest) {
@@ -217,47 +210,36 @@ export function completeBoundary(suspenseBoundaryID, contentID, errorDigest) {
217210
}
218211
}
219212

220-
export function completeContainer(
221-
containerID,
222-
contentID,
223-
optionalErrorMessage,
224-
) {
225-
const contentNode = document.getElementById(contentID);
226-
// We'll detach the content node so that regardless of what happens next we don't leave in the tree.
227-
// This might also help by not causing recalcing each time we move a child from here to the target.
228-
contentNode.parentNode.removeChild(contentNode);
229-
230-
// Find the container node.
231-
const containerNode = document.getElementById(containerID);
232-
if (!containerNode) {
233-
// The user must have already navigated away from this tree.
234-
// E.g. because the parent was hydrated. That's fine there's nothing to do
235-
// but we have to make sure that we already deleted the container node.
236-
return;
237-
}
238-
// This container may have a stashed promise from the style insertions.
239-
// We assume this and clear it now that the root is unblocked and hydration
240-
// can continue. If there is an error message put it in this slot. The client
241-
// will fall back to client rendering the root and use this message in the
242-
// logging
243-
if (optionalErrorMessage) {
244-
throw optionalErrorMessage;
245-
}
213+
export function completeContainer(containerID, contentID) {
214+
const thisDocument = document;
215+
try {
216+
const contentNode = thisDocument.getElementById(contentID);
217+
// We'll detach the content node so that regardless of what happens next we don't leave in the tree.
218+
// This might also help by not causing recalcing each time we move a child from here to the target.
219+
contentNode.parentNode.removeChild(contentNode);
246220

247-
// If the container node has already been passed to createRoot we assume
248-
// the client will render the root and we never swap the content in.
249-
// Additionally if there was an error in loading styles we leave the fallback
250-
// content in place and expect the client will fall back to client rendering
251-
if (containerNode['_c']) {
252-
return;
253-
}
221+
// Find the container node.
222+
const containerNode = thisDocument.getElementById(containerID);
223+
if (!containerNode) {
224+
// The user must have already navigated away from this tree.
225+
// E.g. because the parent was hydrated. That's fine there's nothing to do
226+
// but we have to make sure that we already deleted the container node.
227+
return;
228+
}
254229

255-
// This is a container insertion. we clear simply clear the container
256-
containerNode.textContent = '';
230+
// This is a container insertion. we clear simply clear the container
231+
containerNode.textContent = '';
257232

258-
// Insert all the children from the contentNode between the start and end of suspense boundary.
259-
while (contentNode.firstChild) {
260-
containerNode.appendChild(contentNode.firstChild);
233+
// Insert all the children from the contentNode between the start and end of suspense boundary.
234+
while (contentNode.firstChild) {
235+
containerNode.appendChild(contentNode.firstChild);
236+
}
237+
} finally {
238+
const bootstrapNode = thisDocument.getElementById('bs:' + contentID);
239+
if (bootstrapNode) {
240+
thisDocument.body.appendChild(bootstrapNode.content);
241+
bootstrapNode.parentNode.removeChild(bootstrapNode);
242+
}
261243
}
262244
}
263245

0 commit comments

Comments
 (0)