Skip to content

Commit b448f1e

Browse files
committed
[Fizz] Reuse rootSegmentID as the SuspenseBoundaryID (#27387)
Originally the intension was to have React assign an ID to a user rendered DOM node inside a `fallback` while it was loading. If there already were an explicit `id` defined on the DOM element we would reuse that one instead. That's why this was a DOM Config option and not just built in to Fizz. This became tricky since it can load late and so we'd have to transfer it down and detect it only once it finished rendering and if there is no DOM element it doesn't work anyway. So instead, what we do in practice is to always use a `<template>` tag with the ID. This has the downside of an extra useless node and shifting child CSS selectors. Maybe we'll get around to fixing this properly but it might not be worth it. This PR just gets rid of the SuspenseBoundaryID concept and instead we just use the same ID number as the root segment ID of the boundary to refer to the boundary to simplify the implementation. This also solves the problem that SuspenseBoundaryID isn't currently serializable (although that's easily fixable by itself if necessary). DiffTrain build for [2807d78](2807d78)
1 parent efe6360 commit b448f1e

8 files changed

+351
-501
lines changed

compiled/facebook-www/REVISION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
925c66a6472a61e7174dabf6c697a9a320ebc170
1+
2807d781a08db8e9873687fccc25c0f12b4fb3d4

compiled/facebook-www/ReactDOMServer-dev.classic.js

Lines changed: 23 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ if (__DEV__) {
1919
var React = require("react");
2020
var ReactDOM = require("react-dom");
2121

22-
var ReactVersion = "18.3.0-www-classic-fcac226e";
22+
var ReactVersion = "18.3.0-www-classic-3e4fe4e2";
2323

2424
// This refers to a WWW module.
2525
var warningWWW = require("warning");
@@ -1955,7 +1955,7 @@ function createRenderState$1(resumableState, nonce, importMap) {
19551955
return {
19561956
placeholderPrefix: stringToPrecomputedChunk(idPrefix + "P:"),
19571957
segmentPrefix: stringToPrecomputedChunk(idPrefix + "S:"),
1958-
boundaryPrefix: idPrefix + "B:",
1958+
boundaryPrefix: stringToPrecomputedChunk(idPrefix + "B:"),
19591959
startInlineScript: inlineScriptWithNonce,
19601960
htmlChunks: null,
19611961
headChunks: null,
@@ -2033,7 +2033,7 @@ function createResumableState(
20332033
externalRuntimeScript: externalRuntimeScript,
20342034
bootstrapChunks: bootstrapChunks,
20352035
idPrefix: idPrefix,
2036-
nextSuspenseID: 0,
2036+
nextFormID: 0,
20372037
streamingFormat: streamingFormat,
20382038
instructions: NothingSent,
20392039
hasBody: false,
@@ -2289,13 +2289,6 @@ function getChildFormatContext(parentContext, type, props) {
22892289

22902290
return parentContext;
22912291
}
2292-
var UNINITIALIZED_SUSPENSE_BOUNDARY_ID = null;
2293-
function assignSuspenseBoundaryID(renderState, resumableState) {
2294-
var generatedID = resumableState.nextSuspenseID++;
2295-
return stringToPrecomputedChunk(
2296-
renderState.boundaryPrefix + generatedID.toString(16)
2297-
);
2298-
}
22992292
function makeId(resumableState, treeId, localId) {
23002293
var idPrefix = resumableState.idPrefix;
23012294
var id = ":" + idPrefix + "R" + treeId; // Unless this is the first id at this level, append a number at the end
@@ -5018,7 +5011,8 @@ function writeStartPendingSuspenseBoundary(destination, renderState, id) {
50185011
);
50195012
}
50205013

5021-
writeChunk(destination, id);
5014+
writeChunk(destination, renderState.boundaryPrefix);
5015+
writeChunk(destination, stringToChunk(id.toString(16)));
50225016
return writeChunkAndReturn(destination, startPendingSuspenseBoundary2);
50235017
}
50245018
function writeStartClientRenderedSuspenseBoundary$1(
@@ -5298,8 +5292,7 @@ function writeCompletedBoundaryInstruction(
52985292
destination,
52995293
resumableState,
53005294
renderState,
5301-
boundaryID,
5302-
contentSegmentID,
5295+
id,
53035296
boundaryResources
53045297
) {
53055298
var requiresStyleInsertion;
@@ -5357,14 +5350,9 @@ function writeCompletedBoundaryInstruction(
53575350
}
53585351
}
53595352

5360-
if (boundaryID === null) {
5361-
throw new Error(
5362-
"An ID must have been assigned before we can complete the boundary."
5363-
);
5364-
} // Write function arguments, which are string and array literals
5365-
5366-
var formattedContentID = stringToChunk(contentSegmentID.toString(16));
5367-
writeChunk(destination, boundaryID);
5353+
var idChunk = stringToChunk(id.toString(16));
5354+
writeChunk(destination, renderState.boundaryPrefix);
5355+
writeChunk(destination, idChunk); // Write function arguments, which are string and array literals
53685356

53695357
if (scriptFormat) {
53705358
writeChunk(destination, completeBoundaryScript2);
@@ -5373,7 +5361,7 @@ function writeCompletedBoundaryInstruction(
53735361
}
53745362

53755363
writeChunk(destination, renderState.segmentPrefix);
5376-
writeChunk(destination, formattedContentID);
5364+
writeChunk(destination, idChunk);
53775365

53785366
if (requiresStyleInsertion) {
53795367
// Script and data writers must format this differently:
@@ -5423,7 +5411,7 @@ function writeClientRenderBoundaryInstruction(
54235411
destination,
54245412
resumableState,
54255413
renderState,
5426-
boundaryID,
5414+
id,
54275415
errorDigest,
54285416
errorMessage,
54295417
errorComponentStack
@@ -5449,13 +5437,8 @@ function writeClientRenderBoundaryInstruction(
54495437
writeChunk(destination, clientRenderData1);
54505438
}
54515439

5452-
if (boundaryID === null) {
5453-
throw new Error(
5454-
"An ID must have been assigned before we can complete the boundary."
5455-
);
5456-
}
5457-
5458-
writeChunk(destination, boundaryID);
5440+
writeChunk(destination, renderState.boundaryPrefix);
5441+
writeChunk(destination, stringToChunk(id.toString(16)));
54595442

54605443
if (scriptFormat) {
54615444
// " needs to be inserted for scripts, since ArgInterstitual does not contain
@@ -9839,7 +9822,6 @@ function pingTask(request, task) {
98399822
function createSuspenseBoundary(request, fallbackAbortableTasks, keyPath) {
98409823
return {
98419824
status: PENDING,
9842-
id: UNINITIALIZED_SUSPENSE_BOUNDARY_ID,
98439825
rootSegmentID: -1,
98449826
parentFlushed: false,
98459827
pendingTasks: 0,
@@ -10247,8 +10229,7 @@ function replaySuspenseBoundary(request, task, keyPath, props, replayNode) {
1024710229
);
1024810230
resumedBoundary.parentFlushed = true; // We restore the same id of this boundary as was used during prerender.
1024910231

10250-
resumedBoundary.id = replayNode[4];
10251-
resumedBoundary.rootSegmentID = replayNode[5]; // We can reuse the current context and task to render the content immediately without
10232+
resumedBoundary.rootSegmentID = replayNode[4]; // We can reuse the current context and task to render the content immediately without
1025210233
// context switching. We just need to temporarily switch which boundary and replay node
1025310234
// we're writing to. If something suspends, it'll spawn new suspended task with that context.
1025410235

@@ -10332,10 +10313,10 @@ function resumeSuspenseBoundary(request, task, keyPath, props, replayNode) {
1033210313
fallbackAbortSet,
1033310314
task.keyPath
1033410315
);
10335-
resumedBoundary.parentFlushed = true; // We restore the same id of this boundary as was used during prerender.
10316+
resumedBoundary.parentFlushed = true;
10317+
var id = replayNode[3]; // We restore the same id of this boundary as was used during prerender.
1033610318

10337-
resumedBoundary.id = replayNode[3];
10338-
resumedBoundary.rootSegmentID = replayNode[4];
10319+
resumedBoundary.rootSegmentID = id;
1033910320
var resumedSegment = createPendingSegment(
1034010321
request,
1034110322
0,
@@ -10345,7 +10326,7 @@ function resumeSuspenseBoundary(request, task, keyPath, props, replayNode) {
1034510326
false
1034610327
);
1034710328
resumedSegment.parentFlushed = true;
10348-
resumedSegment.id = replayNode[4]; // We can reuse the current context and task to render the content immediately without
10329+
resumedSegment.id = id; // We can reuse the current context and task to render the content immediately without
1034910330
// context switching. We just need to temporarily switch which boundary and replay node
1035010331
// we're writing to. If something suspends, it'll spawn new suspended task with that context.
1035110332

@@ -12080,7 +12061,6 @@ function abortTaskSoft(task) {
1208012061

1208112062
function abortRemainingSuspenseBoundary(
1208212063
request,
12083-
id,
1208412064
rootSegmentID,
1208512065
error,
1208612066
errorDigest
@@ -12092,7 +12072,6 @@ function abortRemainingSuspenseBoundary(
1209212072
);
1209312073
resumedBoundary.parentFlushed = true; // We restore the same id of this boundary as was used during prerender.
1209412074

12095-
resumedBoundary.id = id;
1209612075
resumedBoundary.rootSegmentID = rootSegmentID;
1209712076
resumedBoundary.status = CLIENT_RENDERED;
1209812077
resumedBoundary.errorDigest = errorDigest;
@@ -12147,11 +12126,9 @@ function abortRemainingResumableNodes(
1214712126

1214812127
case REPLAY_SUSPENSE_BOUNDARY: {
1214912128
var boundaryNode = node;
12150-
var id = boundaryNode[4];
12151-
var rootSegmentID = boundaryNode[5];
12129+
var rootSegmentID = boundaryNode[4];
1215212130
abortRemainingSuspenseBoundary(
1215312131
request,
12154-
id,
1215512132
rootSegmentID,
1215612133
error,
1215712134
errorDigest
@@ -12161,11 +12138,9 @@ function abortRemainingResumableNodes(
1216112138

1216212139
case RESUME_SUSPENSE_BOUNDARY: {
1216312140
var _boundaryNode2 = node;
12164-
var _id = _boundaryNode2[3];
12165-
var _rootSegmentID = _boundaryNode2[4];
12141+
var _rootSegmentID = _boundaryNode2[3];
1216612142
abortRemainingSuspenseBoundary(
1216712143
request,
12168-
_id,
1216912144
_rootSegmentID,
1217012145
error,
1217112146
errorDigest
@@ -12737,20 +12712,15 @@ function flushSegment(request, destination, segment) {
1273712712
if (boundary.status === PENDING) {
1273812713
// For pending boundaries we lazily assign an ID to the boundary
1273912714
// and root segment.
12740-
boundary.id = assignSuspenseBoundaryID(
12741-
request.renderState,
12742-
request.resumableState
12743-
);
1274412715
boundary.rootSegmentID = request.nextSegmentId++;
1274512716
}
1274612717

1274712718
if (boundary.completedSegments.length > 0) {
1274812719
// If this is at least partially complete, we can queue it to be partially emitted early.
1274912720
request.partialBoundaries.push(boundary);
1275012721
} // This boundary is still loading. Emit a pending suspense boundary wrapper.
12751-
/// This is the first time we should have referenced this ID.
1275212722

12753-
var id = boundary.id;
12723+
var id = boundary.rootSegmentID;
1275412724
writeStartPendingSuspenseBoundary(destination, request.renderState, id); // Flush the fallback.
1275512725

1275612726
flushSubtree(request, destination, segment);
@@ -12768,7 +12738,7 @@ function flushSegment(request, destination, segment) {
1276812738
writeStartPendingSuspenseBoundary(
1276912739
destination,
1277012740
request.renderState,
12771-
boundary.id
12741+
boundary.rootSegmentID
1277212742
); // Flush the fallback.
1277312743

1277412744
flushSubtree(request, destination, segment);
@@ -12798,7 +12768,7 @@ function flushClientRenderedBoundary(request, destination, boundary) {
1279812768
destination,
1279912769
request.resumableState,
1280012770
request.renderState,
12801-
boundary.id,
12771+
boundary.rootSegmentID,
1280212772
boundary.errorDigest,
1280312773
boundary.errorMessage,
1280412774
boundary.errorComponentStack
@@ -12846,7 +12816,6 @@ function flushCompletedBoundary(request, destination, boundary) {
1284612816
destination,
1284712817
request.resumableState,
1284812818
request.renderState,
12849-
boundary.id,
1285012819
boundary.rootSegmentID,
1285112820
boundary.resources
1285212821
);

0 commit comments

Comments
 (0)