Skip to content

Commit 6518505

Browse files
committed
Refactor change to convey boundary state is not specific to resources
Hoistable elements and resources in fallbacks should not flush unless the fallback itself flushes. Previously we used a renderState-local binding to track the resources for the currently rendering boundary. Instead we now track this in the task itself and pass it to the functions that depend on it. This cleans up an unfortunate factoring that I put in before where during flushing we had to mimic the currently rendering Boundary to hoist correctly. This new factoring does the same thing but in a much clearer way. I do track the Boundary state separately from the Boundary itself on the task as a hot path optimization to avoid having to existence check the boundary in `pushStartInstance`. Conceptually tracking the boundary itself is sufficient but this eliminates an extra condition check. The implementation ended up not merging the boundary resource concept with hoistable state due to the very different nature in which these things need to hoist (one during task completion and the other during flush but only when flushing late boundaries). Given that I've gone back to resource specific naming rather than calling it BoundaryState.
1 parent 2e5b24b commit 6518505

File tree

6 files changed

+492
-249
lines changed

6 files changed

+492
-249
lines changed

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

Lines changed: 63 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,7 @@ export type RenderState = {
149149
bootstrapChunks: Array<Chunk | PrecomputedChunk>,
150150

151151
// Hoistable chunks
152-
charsetChunks: Array<Chunk | PrecomputedChunk>,
153-
preconnectChunks: Array<Chunk | PrecomputedChunk>,
154152
importMapChunks: Array<Chunk | PrecomputedChunk>,
155-
preloadChunks: Array<Chunk | PrecomputedChunk>,
156-
hoistableChunks: Array<Chunk | PrecomputedChunk>,
157153

158154
// Headers queues for Resources that can flush early
159155
onHeaders: void | ((headers: HeadersDescriptor) => void),
@@ -201,9 +197,6 @@ export type RenderState = {
201197
moduleScripts: Map<string, Resource>,
202198
},
203199

204-
// Module-global-like reference for current boundary resources
205-
boundaryResources: ?BoundaryResources,
206-
207200
// Module-global-like reference for flushing/hoisting state of style resources
208201
// We need to track whether the current request has flushed any style resources
209202
// without sending an instruction to hoist them. we do that here
@@ -457,6 +450,7 @@ export function createRenderState(
457450

458451
externalRuntimeScript: externalRuntimeScript,
459452
bootstrapChunks: bootstrapChunks,
453+
importMapChunks,
460454

461455
onHeaders,
462456
headers,
@@ -472,12 +466,6 @@ export function createRenderState(
472466
style: {},
473467
},
474468

475-
charsetChunks: [],
476-
preconnectChunks: [],
477-
importMapChunks,
478-
preloadChunks: [],
479-
hoistableChunks: [],
480-
481469
// cleared on flush
482470
preconnects: new Set(),
483471
fontPreloads: new Set(),
@@ -497,7 +485,6 @@ export function createRenderState(
497485

498486
nonce,
499487
// like a module global for currently rendering boundary
500-
boundaryResources: null,
501488
stylesToHoist: false,
502489
};
503490

@@ -2226,7 +2213,7 @@ function pushStartTextArea(
22262213
function pushMeta(
22272214
target: Array<Chunk | PrecomputedChunk>,
22282215
props: Object,
2229-
renderState: RenderState,
2216+
hoistableState: HoistableState,
22302217
textEmbedded: boolean,
22312218
insertionMode: InsertionMode,
22322219
noscriptTagInScope: boolean,
@@ -2246,12 +2233,12 @@ function pushMeta(
22462233
}
22472234

22482235
if (typeof props.charSet === 'string') {
2249-
return pushSelfClosing(renderState.charsetChunks, props, 'meta');
2236+
return pushSelfClosing(hoistableState.charset, props, 'meta');
22502237
} else if (props.name === 'viewport') {
22512238
// "viewport" isn't related to preconnect but it has the right priority
2252-
return pushSelfClosing(renderState.preconnectChunks, props, 'meta');
2239+
return pushSelfClosing(hoistableState.viewport, props, 'meta');
22532240
} else {
2254-
return pushSelfClosing(renderState.hoistableChunks, props, 'meta');
2241+
return pushSelfClosing(hoistableState.chunks, props, 'meta');
22552242
}
22562243
}
22572244
} else {
@@ -2264,6 +2251,8 @@ function pushLink(
22642251
props: Object,
22652252
resumableState: ResumableState,
22662253
renderState: RenderState,
2254+
boundaryResources: null | BoundaryResources,
2255+
hoistableState: HoistableState,
22672256
textEmbedded: boolean,
22682257
insertionMode: InsertionMode,
22692258
noscriptTagInScope: boolean,
@@ -2384,8 +2373,8 @@ function pushLink(
23842373
// We add the newly created resource to our StyleQueue and if necessary
23852374
// track the resource with the currently rendering boundary
23862375
styleQueue.sheets.set(key, resource);
2387-
if (renderState.boundaryResources) {
2388-
renderState.boundaryResources.stylesheets.add(resource);
2376+
if (boundaryResources) {
2377+
boundaryResources.stylesheets.add(resource);
23892378
}
23902379
} else {
23912380
// We need to track whether this boundary should wait on this resource or not.
@@ -2396,8 +2385,8 @@ function pushLink(
23962385
if (styleQueue) {
23972386
const resource = styleQueue.sheets.get(key);
23982387
if (resource) {
2399-
if (renderState.boundaryResources) {
2400-
renderState.boundaryResources.stylesheets.add(resource);
2388+
if (boundaryResources) {
2389+
boundaryResources.stylesheets.add(resource);
24012390
}
24022391
}
24032392
}
@@ -2422,15 +2411,7 @@ function pushLink(
24222411
target.push(textSeparator);
24232412
}
24242413

2425-
switch (props.rel) {
2426-
case 'preconnect':
2427-
case 'dns-prefetch':
2428-
return pushLinkImpl(renderState.preconnectChunks, props);
2429-
case 'preload':
2430-
return pushLinkImpl(renderState.preloadChunks, props);
2431-
default:
2432-
return pushLinkImpl(renderState.hoistableChunks, props);
2433-
}
2414+
return pushLinkImpl(hoistableState.chunks, props);
24342415
}
24352416
} else {
24362417
return pushLinkImpl(target, props);
@@ -2472,6 +2453,7 @@ function pushStyle(
24722453
props: Object,
24732454
resumableState: ResumableState,
24742455
renderState: RenderState,
2456+
boundaryResources: null | BoundaryResources,
24752457
textEmbedded: boolean,
24762458
insertionMode: InsertionMode,
24772459
noscriptTagInScope: boolean,
@@ -2571,8 +2553,8 @@ function pushStyle(
25712553
// it. However, it's possible when you resume that the style has already been emitted
25722554
// and then it wouldn't be recreated in the RenderState and there's no need to track
25732555
// it again since we should've hoisted it to the shell already.
2574-
if (renderState.boundaryResources) {
2575-
renderState.boundaryResources.styles.add(styleQueue);
2556+
if (boundaryResources) {
2557+
boundaryResources.styles.add(styleQueue);
25762558
}
25772559
}
25782560

@@ -2882,7 +2864,7 @@ function pushStartMenuItem(
28822864
function pushTitle(
28832865
target: Array<Chunk | PrecomputedChunk>,
28842866
props: Object,
2885-
renderState: RenderState,
2867+
hoistableState: HoistableState,
28862868
insertionMode: InsertionMode,
28872869
noscriptTagInScope: boolean,
28882870
): ReactNodeList {
@@ -2940,7 +2922,7 @@ function pushTitle(
29402922
!noscriptTagInScope &&
29412923
props.itemProp == null
29422924
) {
2943-
pushTitleImpl(renderState.hoistableChunks, props);
2925+
pushTitleImpl(hoistableState.chunks, props);
29442926
return null;
29452927
} else {
29462928
return pushTitleImpl(target, props);
@@ -3472,6 +3454,8 @@ export function pushStartInstance(
34723454
props: Object,
34733455
resumableState: ResumableState,
34743456
renderState: RenderState,
3457+
boundaryResources: null | BoundaryResources,
3458+
hoistableState: HoistableState,
34753459
formatContext: FormatContext,
34763460
textEmbedded: boolean,
34773461
): ReactNodeList {
@@ -3539,7 +3523,7 @@ export function pushStartInstance(
35393523
? pushTitle(
35403524
target,
35413525
props,
3542-
renderState,
3526+
hoistableState,
35433527
formatContext.insertionMode,
35443528
!!(formatContext.tagScope & NOSCRIPT_SCOPE),
35453529
)
@@ -3550,6 +3534,8 @@ export function pushStartInstance(
35503534
props,
35513535
resumableState,
35523536
renderState,
3537+
boundaryResources,
3538+
hoistableState,
35533539
textEmbedded,
35543540
formatContext.insertionMode,
35553541
!!(formatContext.tagScope & NOSCRIPT_SCOPE),
@@ -3572,6 +3558,7 @@ export function pushStartInstance(
35723558
props,
35733559
resumableState,
35743560
renderState,
3561+
boundaryResources,
35753562
textEmbedded,
35763563
formatContext.insertionMode,
35773564
!!(formatContext.tagScope & NOSCRIPT_SCOPE),
@@ -3580,7 +3567,7 @@ export function pushStartInstance(
35803567
return pushMeta(
35813568
target,
35823569
props,
3583-
renderState,
3570+
hoistableState,
35843571
textEmbedded,
35853572
formatContext.insertionMode,
35863573
!!(formatContext.tagScope & NOSCRIPT_SCOPE),
@@ -4574,6 +4561,7 @@ export function writePreamble(
45744561
destination: Destination,
45754562
resumableState: ResumableState,
45764563
renderState: RenderState,
4564+
hoistableState: HoistableState,
45774565
willFlushAllSegments: boolean,
45784566
): void {
45794567
// This function must be called exactly once on every request
@@ -4619,7 +4607,7 @@ export function writePreamble(
46194607
}
46204608

46214609
// Emit high priority Hoistables
4622-
const charsetChunks = renderState.charsetChunks;
4610+
const charsetChunks = hoistableState.charset;
46234611
for (i = 0; i < charsetChunks.length; i++) {
46244612
writeChunk(destination, charsetChunks[i]);
46254613
}
@@ -4629,11 +4617,11 @@ export function writePreamble(
46294617
renderState.preconnects.forEach(flushResource, destination);
46304618
renderState.preconnects.clear();
46314619

4632-
const preconnectChunks = renderState.preconnectChunks;
4633-
for (i = 0; i < preconnectChunks.length; i++) {
4634-
writeChunk(destination, preconnectChunks[i]);
4620+
const viewportChunks = hoistableState.viewport;
4621+
for (i = 0; i < viewportChunks.length; i++) {
4622+
writeChunk(destination, viewportChunks[i]);
46354623
}
4636-
preconnectChunks.length = 0;
4624+
viewportChunks.length = 0;
46374625

46384626
renderState.fontPreloads.forEach(flushResource, destination);
46394627
renderState.fontPreloads.clear();
@@ -4658,15 +4646,8 @@ export function writePreamble(
46584646
renderState.bulkPreloads.forEach(flushResource, destination);
46594647
renderState.bulkPreloads.clear();
46604648

4661-
// Write embedding preloadChunks
4662-
const preloadChunks = renderState.preloadChunks;
4663-
for (i = 0; i < preloadChunks.length; i++) {
4664-
writeChunk(destination, preloadChunks[i]);
4665-
}
4666-
preloadChunks.length = 0;
4667-
4668-
// Write embedding hoistableChunks
4669-
const hoistableChunks = renderState.hoistableChunks;
4649+
// Write hoistableState chunks
4650+
const hoistableChunks = hoistableState.chunks;
46704651
for (i = 0; i < hoistableChunks.length; i++) {
46714652
writeChunk(destination, hoistableChunks[i]);
46724653
}
@@ -4691,6 +4672,7 @@ export function writeHoistables(
46914672
destination: Destination,
46924673
resumableState: ResumableState,
46934674
renderState: RenderState,
4675+
hoistableState: HoistableState,
46944676
): void {
46954677
let i = 0;
46964678

@@ -4702,12 +4684,6 @@ export function writeHoistables(
47024684
renderState.preconnects.forEach(flushResource, destination);
47034685
renderState.preconnects.clear();
47044686

4705-
const preconnectChunks = renderState.preconnectChunks;
4706-
for (i = 0; i < preconnectChunks.length; i++) {
4707-
writeChunk(destination, preconnectChunks[i]);
4708-
}
4709-
preconnectChunks.length = 0;
4710-
47114687
renderState.fontPreloads.forEach(flushResource, destination);
47124688
renderState.fontPreloads.clear();
47134689

@@ -4732,15 +4708,8 @@ export function writeHoistables(
47324708
renderState.bulkPreloads.forEach(flushResource, destination);
47334709
renderState.bulkPreloads.clear();
47344710

4735-
// Write embedding preloadChunks
4736-
const preloadChunks = renderState.preloadChunks;
4737-
for (i = 0; i < preloadChunks.length; i++) {
4738-
writeChunk(destination, preloadChunks[i]);
4739-
}
4740-
preloadChunks.length = 0;
4741-
4742-
// Write embedding hoistableChunks
4743-
const hoistableChunks = renderState.hoistableChunks;
4711+
// Write hoistableState chunks
4712+
const hoistableChunks = hoistableState.chunks;
47444713
for (i = 0; i < hoistableChunks.length; i++) {
47454714
writeChunk(destination, hoistableChunks[i]);
47464715
}
@@ -5214,7 +5183,22 @@ type StylesheetResource = {
52145183
state: StylesheetState,
52155184
};
52165185

5186+
export type HoistableState = {
5187+
charset: Array<Chunk | PrecomputedChunk>,
5188+
viewport: Array<Chunk | PrecomputedChunk>,
5189+
chunks: Array<Chunk | PrecomputedChunk>,
5190+
};
5191+
5192+
export function createHoistableState(): HoistableState {
5193+
return {
5194+
charset: [],
5195+
viewport: [],
5196+
chunks: [],
5197+
};
5198+
}
5199+
52175200
export type BoundaryResources = {
5201+
// style dependencies
52185202
styles: Set<StyleQueue>,
52195203
stylesheets: Set<StylesheetResource>,
52205204
};
@@ -5233,13 +5217,6 @@ export function createBoundaryResources(): BoundaryResources {
52335217
};
52345218
}
52355219

5236-
export function setCurrentlyRenderingBoundaryResourcesTarget(
5237-
renderState: RenderState,
5238-
boundaryResources: null | BoundaryResources,
5239-
) {
5240-
renderState.boundaryResources = boundaryResources;
5241-
}
5242-
52435220
function getResourceKey(href: string): string {
52445221
return href;
52455222
}
@@ -6086,6 +6063,15 @@ function escapeStringForLinkHeaderQuotedParamValueContextReplacer(
60866063
}
60876064
}
60886065

6066+
export function hoistHoistables(
6067+
target: HoistableState,
6068+
source: HoistableState,
6069+
) {
6070+
target.charset.push(...source.charset);
6071+
target.viewport.push(...source.viewport);
6072+
target.chunks.push(...source.chunks);
6073+
}
6074+
60896075
function hoistStyleQueueDependency(
60906076
this: BoundaryResources,
60916077
styleQueue: StyleQueue,
@@ -6100,18 +6086,12 @@ function hoistStylesheetDependency(
61006086
this.stylesheets.add(stylesheet);
61016087
}
61026088

6103-
export function hoistResources(
6104-
renderState: RenderState,
6089+
export function hoistBoundaryResources(
6090+
target: BoundaryResources,
61056091
source: BoundaryResources,
61066092
): void {
6107-
const currentBoundaryResources = renderState.boundaryResources;
6108-
if (currentBoundaryResources) {
6109-
source.styles.forEach(hoistStyleQueueDependency, currentBoundaryResources);
6110-
source.stylesheets.forEach(
6111-
hoistStylesheetDependency,
6112-
currentBoundaryResources,
6113-
);
6114-
}
6093+
source.styles.forEach(hoistStyleQueueDependency, target);
6094+
source.stylesheets.forEach(hoistStylesheetDependency, target);
61156095
}
61166096

61176097
// This function is called at various times depending on whether we are rendering

0 commit comments

Comments
 (0)