Skip to content

Commit 3f67d08

Browse files
authored
[Fizz] Track whether we're in a fallback on FormatContext (#33194)
Removes the `isFallback` flag on Tasks and tracks it on the formatContext instead. Less memory and avoids passing and tracking extra arguments to all the pushStartInstance branches that doesn't need it. We'll need to be able to track more Suspense related contexts on this for View Transitions anyway.
1 parent 96eb84e commit 3f67d08

File tree

6 files changed

+88
-86
lines changed

6 files changed

+88
-86
lines changed

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

Lines changed: 58 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -741,9 +741,10 @@ const HTML_COLGROUP_MODE = 9;
741741

742742
type InsertionMode = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
743743

744-
const NO_SCOPE = /* */ 0b00;
745-
const NOSCRIPT_SCOPE = /* */ 0b01;
746-
const PICTURE_SCOPE = /* */ 0b10;
744+
const NO_SCOPE = /* */ 0b000;
745+
const NOSCRIPT_SCOPE = /* */ 0b001;
746+
const PICTURE_SCOPE = /* */ 0b010;
747+
const FALLBACK_SCOPE = /* */ 0b100;
747748

748749
// Lets us keep track of contextual state and pick it back up after suspending.
749750
export type FormatContext = {
@@ -754,7 +755,7 @@ export type FormatContext = {
754755

755756
function createFormatContext(
756757
insertionMode: InsertionMode,
757-
selectedValue: null | string,
758+
selectedValue: null | string | Array<string>,
758759
tagScope: number,
759760
): FormatContext {
760761
return {
@@ -864,6 +865,22 @@ export function getChildFormatContext(
864865
return parentContext;
865866
}
866867

868+
export function getSuspenseFallbackFormatContext(
869+
parentContext: FormatContext,
870+
): FormatContext {
871+
return createFormatContext(
872+
parentContext.insertionMode,
873+
parentContext.selectedValue,
874+
parentContext.tagScope | FALLBACK_SCOPE,
875+
);
876+
}
877+
878+
export function getSuspenseContentFormatContext(
879+
parentContext: FormatContext,
880+
): FormatContext {
881+
return parentContext;
882+
}
883+
867884
export function isPreambleContext(formatContext: FormatContext): boolean {
868885
return formatContext.insertionMode === HTML_HEAD_MODE;
869886
}
@@ -2511,12 +2528,12 @@ function pushMeta(
25112528
props: Object,
25122529
renderState: RenderState,
25132530
textEmbedded: boolean,
2514-
insertionMode: InsertionMode,
2515-
noscriptTagInScope: boolean,
2516-
isFallback: boolean,
2531+
formatContext: FormatContext,
25172532
): null {
2533+
const noscriptTagInScope = formatContext.tagScope & NOSCRIPT_SCOPE;
2534+
const isFallback = formatContext.tagScope & FALLBACK_SCOPE;
25182535
if (
2519-
insertionMode === SVG_MODE ||
2536+
formatContext.insertionMode === SVG_MODE ||
25202537
noscriptTagInScope ||
25212538
props.itemProp != null
25222539
) {
@@ -2559,15 +2576,15 @@ function pushLink(
25592576
renderState: RenderState,
25602577
hoistableState: null | HoistableState,
25612578
textEmbedded: boolean,
2562-
insertionMode: InsertionMode,
2563-
noscriptTagInScope: boolean,
2564-
isFallback: boolean,
2579+
formatContext: FormatContext,
25652580
): null {
2581+
const noscriptTagInScope = formatContext.tagScope & NOSCRIPT_SCOPE;
2582+
const isFallback = formatContext.tagScope & FALLBACK_SCOPE;
25662583
const rel = props.rel;
25672584
const href = props.href;
25682585
const precedence = props.precedence;
25692586
if (
2570-
insertionMode === SVG_MODE ||
2587+
formatContext.insertionMode === SVG_MODE ||
25712588
noscriptTagInScope ||
25722589
props.itemProp != null ||
25732590
typeof rel !== 'string' ||
@@ -2765,9 +2782,9 @@ function pushStyle(
27652782
renderState: RenderState,
27662783
hoistableState: null | HoistableState,
27672784
textEmbedded: boolean,
2768-
insertionMode: InsertionMode,
2769-
noscriptTagInScope: boolean,
2785+
formatContext: FormatContext,
27702786
): ReactNodeList {
2787+
const noscriptTagInScope = formatContext.tagScope & NOSCRIPT_SCOPE;
27712788
if (__DEV__) {
27722789
if (hasOwnProperty.call(props, 'children')) {
27732790
const children = props.children;
@@ -2801,7 +2818,7 @@ function pushStyle(
28012818
const href = props.href;
28022819

28032820
if (
2804-
insertionMode === SVG_MODE ||
2821+
formatContext.insertionMode === SVG_MODE ||
28052822
noscriptTagInScope ||
28062823
props.itemProp != null ||
28072824
typeof precedence !== 'string' ||
@@ -2984,16 +3001,18 @@ function pushImg(
29843001
props: Object,
29853002
resumableState: ResumableState,
29863003
renderState: RenderState,
2987-
pictureOrNoScriptTagInScope: boolean,
3004+
formatContext: FormatContext,
29883005
): null {
3006+
const pictureOrNoScriptTagInScope =
3007+
formatContext.tagScope & (PICTURE_SCOPE | NOSCRIPT_SCOPE);
29893008
const {src, srcSet} = props;
29903009
if (
29913010
props.loading !== 'lazy' &&
29923011
(src || srcSet) &&
29933012
(typeof src === 'string' || src == null) &&
29943013
(typeof srcSet === 'string' || srcSet == null) &&
29953014
props.fetchPriority !== 'low' &&
2996-
pictureOrNoScriptTagInScope === false &&
3015+
!pictureOrNoScriptTagInScope &&
29973016
// We exclude data URIs in src and srcSet since these should not be preloaded
29983017
!(
29993018
typeof src === 'string' &&
@@ -3190,10 +3209,10 @@ function pushTitle(
31903209
target: Array<Chunk | PrecomputedChunk>,
31913210
props: Object,
31923211
renderState: RenderState,
3193-
insertionMode: InsertionMode,
3194-
noscriptTagInScope: boolean,
3195-
isFallback: boolean,
3212+
formatContext: FormatContext,
31963213
): ReactNodeList {
3214+
const noscriptTagInScope = formatContext.tagScope & NOSCRIPT_SCOPE;
3215+
const isFallback = formatContext.tagScope & FALLBACK_SCOPE;
31973216
if (__DEV__) {
31983217
if (hasOwnProperty.call(props, 'children')) {
31993218
const children = props.children;
@@ -3243,7 +3262,7 @@ function pushTitle(
32433262
}
32443263

32453264
if (
3246-
insertionMode !== SVG_MODE &&
3265+
formatContext.insertionMode !== SVG_MODE &&
32473266
!noscriptTagInScope &&
32483267
props.itemProp == null
32493268
) {
@@ -3320,9 +3339,9 @@ function pushStartHead(
33203339
props: Object,
33213340
renderState: RenderState,
33223341
preambleState: null | PreambleState,
3323-
insertionMode: InsertionMode,
3342+
formatContext: FormatContext,
33243343
): ReactNodeList {
3325-
if (insertionMode < HTML_MODE) {
3344+
if (formatContext.insertionMode < HTML_MODE) {
33263345
// This <head> is the Document.head and should be part of the preamble
33273346
const preamble = preambleState || renderState.preamble;
33283347

@@ -3349,9 +3368,9 @@ function pushStartBody(
33493368
props: Object,
33503369
renderState: RenderState,
33513370
preambleState: null | PreambleState,
3352-
insertionMode: InsertionMode,
3371+
formatContext: FormatContext,
33533372
): ReactNodeList {
3354-
if (insertionMode < HTML_MODE) {
3373+
if (formatContext.insertionMode < HTML_MODE) {
33553374
// This <body> is the Document.body
33563375
const preamble = preambleState || renderState.preamble;
33573376

@@ -3378,9 +3397,9 @@ function pushStartHtml(
33783397
props: Object,
33793398
renderState: RenderState,
33803399
preambleState: null | PreambleState,
3381-
insertionMode: InsertionMode,
3400+
formatContext: FormatContext,
33823401
): ReactNodeList {
3383-
if (insertionMode === ROOT_HTML_MODE) {
3402+
if (formatContext.insertionMode === ROOT_HTML_MODE) {
33843403
// This <html> is the Document.documentElement
33853404
const preamble = preambleState || renderState.preamble;
33863405

@@ -3408,9 +3427,9 @@ function pushScript(
34083427
resumableState: ResumableState,
34093428
renderState: RenderState,
34103429
textEmbedded: boolean,
3411-
insertionMode: InsertionMode,
3412-
noscriptTagInScope: boolean,
3430+
formatContext: FormatContext,
34133431
): null {
3432+
const noscriptTagInScope = formatContext.tagScope & NOSCRIPT_SCOPE;
34143433
const asyncProp = props.async;
34153434
if (
34163435
typeof props.src !== 'string' ||
@@ -3422,7 +3441,7 @@ function pushScript(
34223441
) ||
34233442
props.onLoad ||
34243443
props.onError ||
3425-
insertionMode === SVG_MODE ||
3444+
formatContext.insertionMode === SVG_MODE ||
34263445
noscriptTagInScope ||
34273446
props.itemProp != null
34283447
) {
@@ -3790,7 +3809,6 @@ export function pushStartInstance(
37903809
hoistableState: null | HoistableState,
37913810
formatContext: FormatContext,
37923811
textEmbedded: boolean,
3793-
isFallback: boolean,
37943812
): ReactNodeList {
37953813
if (__DEV__) {
37963814
validateARIAProperties(type, props);
@@ -3857,14 +3875,7 @@ export function pushStartInstance(
38573875
case 'object':
38583876
return pushStartObject(target, props);
38593877
case 'title':
3860-
return pushTitle(
3861-
target,
3862-
props,
3863-
renderState,
3864-
formatContext.insertionMode,
3865-
!!(formatContext.tagScope & NOSCRIPT_SCOPE),
3866-
isFallback,
3867-
);
3878+
return pushTitle(target, props, renderState, formatContext);
38683879
case 'link':
38693880
return pushLink(
38703881
target,
@@ -3873,9 +3884,7 @@ export function pushStartInstance(
38733884
renderState,
38743885
hoistableState,
38753886
textEmbedded,
3876-
formatContext.insertionMode,
3877-
!!(formatContext.tagScope & NOSCRIPT_SCOPE),
3878-
isFallback,
3887+
formatContext,
38793888
);
38803889
case 'script':
38813890
return pushScript(
@@ -3884,8 +3893,7 @@ export function pushStartInstance(
38843893
resumableState,
38853894
renderState,
38863895
textEmbedded,
3887-
formatContext.insertionMode,
3888-
!!(formatContext.tagScope & NOSCRIPT_SCOPE),
3896+
formatContext,
38893897
);
38903898
case 'style':
38913899
return pushStyle(
@@ -3895,32 +3903,17 @@ export function pushStartInstance(
38953903
renderState,
38963904
hoistableState,
38973905
textEmbedded,
3898-
formatContext.insertionMode,
3899-
!!(formatContext.tagScope & NOSCRIPT_SCOPE),
3906+
formatContext,
39003907
);
39013908
case 'meta':
3902-
return pushMeta(
3903-
target,
3904-
props,
3905-
renderState,
3906-
textEmbedded,
3907-
formatContext.insertionMode,
3908-
!!(formatContext.tagScope & NOSCRIPT_SCOPE),
3909-
isFallback,
3910-
);
3909+
return pushMeta(target, props, renderState, textEmbedded, formatContext);
39113910
// Newline eating tags
39123911
case 'listing':
39133912
case 'pre': {
39143913
return pushStartPreformattedElement(target, props, type);
39153914
}
39163915
case 'img': {
3917-
return pushImg(
3918-
target,
3919-
props,
3920-
resumableState,
3921-
renderState,
3922-
!!(formatContext.tagScope & (PICTURE_SCOPE | NOSCRIPT_SCOPE)),
3923-
);
3916+
return pushImg(target, props, resumableState, renderState, formatContext);
39243917
}
39253918
// Omitted close tags
39263919
case 'base':
@@ -3955,23 +3948,23 @@ export function pushStartInstance(
39553948
props,
39563949
renderState,
39573950
preambleState,
3958-
formatContext.insertionMode,
3951+
formatContext,
39593952
);
39603953
case 'body':
39613954
return pushStartBody(
39623955
target,
39633956
props,
39643957
renderState,
39653958
preambleState,
3966-
formatContext.insertionMode,
3959+
formatContext,
39673960
);
39683961
case 'html': {
39693962
return pushStartHtml(
39703963
target,
39713964
props,
39723965
renderState,
39733966
preambleState,
3974-
formatContext.insertionMode,
3967+
formatContext,
39753968
);
39763969
}
39773970
default: {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ export type {
141141

142142
export {
143143
getChildFormatContext,
144+
getSuspenseFallbackFormatContext,
145+
getSuspenseContentFormatContext,
144146
makeId,
145147
pushStartInstance,
146148
pushEndInstance,

packages/react-markup/src/ReactFizzConfigMarkup.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ export type {
5252

5353
export {
5454
getChildFormatContext,
55+
getSuspenseFallbackFormatContext,
56+
getSuspenseContentFormatContext,
5557
makeId,
5658
pushEndInstance,
5759
pushFormStateMarkerIsMatching,
@@ -96,7 +98,6 @@ export function pushStartInstance(
9698
hoistableState: null | HoistableState,
9799
formatContext: FormatContext,
98100
textEmbedded: boolean,
99-
isFallback: boolean,
100101
): ReactNodeList {
101102
for (const propKey in props) {
102103
if (hasOwnProperty.call(props, propKey)) {
@@ -127,7 +128,6 @@ export function pushStartInstance(
127128
hoistableState,
128129
formatContext,
129130
textEmbedded,
130-
isFallback,
131131
);
132132
}
133133

packages/react-noop-renderer/src/ReactNoopServer.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@ const ReactNoopServer = ReactFizzServer({
104104
getChildFormatContext(): null {
105105
return null;
106106
},
107+
getSuspenseFallbackFormatContext(): null {
108+
return null;
109+
},
110+
getSuspenseContentFormatContext(): null {
111+
return null;
112+
},
107113

108114
resetResumableState(): void {},
109115
completeResumableState(): void {},

0 commit comments

Comments
 (0)