Skip to content

Commit c67471f

Browse files
committed
Move lazy ID initialization to the core implementation
We never need an ID before we write a pending boundary. This also ensures that ID generation is deterministic by moving it to the write phase.
1 parent da90f16 commit c67471f

File tree

6 files changed

+36
-36
lines changed

6 files changed

+36
-36
lines changed

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

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,17 @@ export function getChildFormatContext(
168168
return parentContext;
169169
}
170170

171-
export type SuspenseBoundaryID = {
172-
formattedID: null | PrecomputedChunk,
173-
};
171+
export type SuspenseBoundaryID = null | PrecomputedChunk;
172+
173+
export const UNINITIALIZED_SUSPENSE_BOUNDARY_ID: SuspenseBoundaryID = null;
174174

175-
export function createSuspenseBoundaryID(
175+
export function assignSuspenseBoundaryID(
176176
responseState: ResponseState,
177177
): SuspenseBoundaryID {
178-
return {formattedID: null};
178+
const generatedID = responseState.nextSuspenseID++;
179+
return stringToPrecomputedChunk(
180+
responseState.boundaryPrefix + generatedID.toString(16),
181+
);
179182
}
180183

181184
export type OpaqueIDType = string;
@@ -198,16 +201,6 @@ function encodeHTMLTextNode(text: string): string {
198201
return escapeTextForBrowser(text);
199202
}
200203

201-
function assignAnID(
202-
responseState: ResponseState,
203-
id: SuspenseBoundaryID,
204-
): PrecomputedChunk {
205-
const generatedID = responseState.nextSuspenseID++;
206-
return (id.formattedID = stringToPrecomputedChunk(
207-
responseState.boundaryPrefix + generatedID.toString(16),
208-
));
209-
}
210-
211204
const textSeparator = stringToPrecomputedChunk('<!-- -->');
212205

213206
export function pushTextInstance(
@@ -1377,8 +1370,11 @@ export function writeStartPendingSuspenseBoundary(
13771370
id: SuspenseBoundaryID,
13781371
): boolean {
13791372
writeChunk(destination, startPendingSuspenseBoundary1);
1380-
const formattedID = assignAnID(responseState, id);
1381-
writeChunk(destination, formattedID);
1373+
invariant(
1374+
id !== null,
1375+
'An ID must have been assigned before we can complete the boundary.',
1376+
);
1377+
writeChunk(destination, id);
13821378
return writeChunk(destination, startPendingSuspenseBoundary2);
13831379
}
13841380
export function writeStartClientRenderedSuspenseBoundary(
@@ -1708,13 +1704,12 @@ export function writeCompletedBoundaryInstruction(
17081704
// Future calls can just reuse the same function.
17091705
writeChunk(destination, completeBoundaryScript1Partial);
17101706
}
1711-
const formattedBoundaryID = boundaryID.formattedID;
17121707
invariant(
1713-
formattedBoundaryID !== null,
1708+
boundaryID !== null,
17141709
'An ID must have been assigned before we can complete the boundary.',
17151710
);
17161711
const formattedContentID = stringToChunk(contentSegmentID.toString(16));
1717-
writeChunk(destination, formattedBoundaryID);
1712+
writeChunk(destination, boundaryID);
17181713
writeChunk(destination, completeBoundaryScript2);
17191714
writeChunk(destination, responseState.segmentPrefix);
17201715
writeChunk(destination, formattedContentID);
@@ -1740,11 +1735,10 @@ export function writeClientRenderBoundaryInstruction(
17401735
// Future calls can just reuse the same function.
17411736
writeChunk(destination, clientRenderScript1Partial);
17421737
}
1743-
const formattedBoundaryID = boundaryID.formattedID;
17441738
invariant(
1745-
formattedBoundaryID !== null,
1739+
boundaryID !== null,
17461740
'An ID must have been assigned before we can complete the boundary.',
17471741
);
1748-
writeChunk(destination, formattedBoundaryID);
1742+
writeChunk(destination, boundaryID);
17491743
return writeChunk(destination, clientRenderScript2);
17501744
}

packages/react-dom/src/server/ReactDOMServerLegacyFormatConfig.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ export type {
7878

7979
export {
8080
getChildFormatContext,
81-
createSuspenseBoundaryID,
81+
UNINITIALIZED_SUSPENSE_BOUNDARY_ID,
82+
assignSuspenseBoundaryID,
8283
makeServerID,
8384
pushStartInstance,
8485
pushEndInstance,

packages/react-native-renderer/src/server/ReactNativeServerFormatConfig.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,11 @@ export function getChildFormatContext(
103103
// This is very specific to DOM where we can't assign an ID to.
104104
export type SuspenseBoundaryID = number;
105105

106-
export function createSuspenseBoundaryID(
106+
export const UNINITIALIZED_SUSPENSE_BOUNDARY_ID = -1;
107+
108+
export function assignSuspenseBoundaryID(
107109
responseState: ResponseState,
108110
): SuspenseBoundaryID {
109-
// TODO: This is not deterministic since it's created during render.
110111
return responseState.nextSuspenseID++;
111112
}
112113

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ const ReactNoopServer = ReactFizzServer({
8181
closeWithError(destination: Destination, error: mixed): void {},
8282
flushBuffered(destination: Destination): void {},
8383

84-
createSuspenseBoundaryID(): SuspenseInstance {
84+
UNINITIALIZED_SUSPENSE_BOUNDARY_ID: null,
85+
86+
assignSuspenseBoundaryID(): SuspenseInstance {
8587
// The ID is a pointer to the boundary itself.
8688
return {state: 'pending', children: []};
8789
},

packages/react-server/src/ReactFizzServer.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ import {
5353
pushEndInstance,
5454
pushStartCompletedSuspenseBoundary,
5555
pushEndCompletedSuspenseBoundary,
56-
createSuspenseBoundaryID,
56+
UNINITIALIZED_SUSPENSE_BOUNDARY_ID,
57+
assignSuspenseBoundaryID,
5758
getChildFormatContext,
5859
} from './ReactServerFormatConfig';
5960
import {
@@ -123,7 +124,7 @@ type LegacyContext = {
123124
};
124125

125126
type SuspenseBoundary = {
126-
+id: SuspenseBoundaryID,
127+
id: SuspenseBoundaryID,
127128
rootSegmentID: number,
128129
forceClientRender: boolean, // if it errors or infinitely suspends
129130
parentFlushed: boolean,
@@ -281,7 +282,7 @@ function createSuspenseBoundary(
281282
fallbackAbortableTasks: Set<Task>,
282283
): SuspenseBoundary {
283284
return {
284-
id: createSuspenseBoundaryID(request.responseState),
285+
id: UNINITIALIZED_SUSPENSE_BOUNDARY_ID,
285286
rootSegmentID: -1,
286287
parentFlushed: false,
287288
pendingTasks: 0,
@@ -1595,11 +1596,10 @@ function flushSegment(
15951596
request.partialBoundaries.push(boundary);
15961597
}
15971598

1598-
writeStartPendingSuspenseBoundary(
1599-
destination,
1600-
request.responseState,
1601-
boundary.id,
1602-
);
1599+
/// This is the first time we should have referenced this ID.
1600+
const id = (boundary.id = assignSuspenseBoundaryID(request.responseState));
1601+
1602+
writeStartPendingSuspenseBoundary(destination, request.responseState, id);
16031603

16041604
// Flush the fallback.
16051605
flushSubtree(request, destination, segment);

packages/react-server/src/forks/ReactServerFormatConfig.custom.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ export opaque type OpaqueIDType = mixed;
3333
export const isPrimaryRenderer = false;
3434

3535
export const getChildFormatContext = $$$hostConfig.getChildFormatContext;
36-
export const createSuspenseBoundaryID = $$$hostConfig.createSuspenseBoundaryID;
36+
export const UNINITIALIZED_SUSPENSE_BOUNDARY_ID =
37+
$$$hostConfig.UNINITIALIZED_SUSPENSE_BOUNDARY_ID;
38+
export const assignSuspenseBoundaryID = $$$hostConfig.assignSuspenseBoundaryID;
3739
export const makeServerID = $$$hostConfig.makeServerID;
3840
export const pushTextInstance = $$$hostConfig.pushTextInstance;
3941
export const pushStartInstance = $$$hostConfig.pushStartInstance;

0 commit comments

Comments
 (0)