@samitouri / QOS-React / commits / 1e986f514f

[Fizz] Unify prerender only fields to save a field (#35067)

I need to regain a field because the SuspenseBoundary type is already at 16 fields in prod, after which it deopts v8. There are two fields that are only used in prerender to track postpones. These are ripe to be split into an optional object so that they only take up one field when they're not used.

Sebastian Markbåge committed Nov 7, 2025 at 09:18 UTC 1e986f514f7fdf73d013bf6595a47dfcb0cf2da7
1 file changed +30 -14
packages/react-server/src/ReactFizzServer.js
+30 -14
@@ -257,8 +257,10 @@ type SuspenseBoundary = {
257 fallbackState: HoistableState,
258 contentPreamble: null | Preamble,
259 fallbackPreamble: null | Preamble,
260 - trackedContentKeyPath: null | KeyNode, // used to track the path for replay nodes
261 - trackedFallbackNode: null | ReplayNode, // used to track the fallback for replay nodes
260 + tracked: null | {
261 + contentKeyPath: null | KeyNode, // used to track the path for replay nodes
262 + fallbackNode: null | ReplayNode, // used to track the fallback for replay nodes
263 + },
264 errorDigest: ?string, // the error hash if it errors
265 // DEV-only fields
266 errorMessage?: null | string, // the error string if it errors
@@ -803,8 +805,7 @@ function createSuspenseBoundary(
805 fallbackState: createHoistableState(),
806 contentPreamble,
807 fallbackPreamble,
806 - trackedContentKeyPath: null,
807 - trackedFallbackNode: null,
808 + tracked: null,
809 };
810 if (__DEV__) {
811 // DEV-only fields for hidden class
@@ -1303,9 +1304,6 @@ function renderSuspenseBoundary(
1304 defer,
1305 );
1306 }
1306 - if (request.trackedPostpones !== null) {
1307 - newBoundary.trackedContentKeyPath = keyPath;
1308 - }
1307
1308 const insertionIndex = parentSegment.chunks.length;
1309 // The children of the boundary segment is actually the fallback.
@@ -1358,9 +1356,12 @@ function renderSuspenseBoundary(
1356 null,
1357 ];
1358 trackedPostpones.workingMap.set(fallbackKeyPath, fallbackReplayNode);
1361 - // We are rendering the fallback before the boundary content so we keep track of
1362 - // the fallback replay node until we determine if the primary content suspends
1363 - newBoundary.trackedFallbackNode = fallbackReplayNode;
1359 + newBoundary.tracked = {
1360 + contentKeyPath: keyPath,
1361 + // We are rendering the fallback before the boundary content so we keep track of
1362 + // the fallback replay node until we determine if the primary content suspends
1363 + fallbackNode: fallbackReplayNode,
1364 + };
1365 }
1366
1367 task.blockedSegment = boundarySegment;
@@ -3793,14 +3794,21 @@ function trackPostponedBoundary(
3794 // it before flushing and we know that we can't inline it.
3795 boundary.rootSegmentID = request.nextSegmentId++;
3796
3796 - const boundaryKeyPath = boundary.trackedContentKeyPath;
3797 + const tracked = boundary.tracked;
3798 + if (tracked === null) {
3799 + throw new Error(
3800 + 'It should not be possible to postpone at the root. This is a bug in React.',
3801 + );
3802 + }
3803 +
3804 + const boundaryKeyPath = tracked.contentKeyPath;
3805 if (boundaryKeyPath === null) {
3806 throw new Error(
3807 'It should not be possible to postpone at the root. This is a bug in React.',
3808 );
3809 }
3810
3803 - const fallbackReplayNode = boundary.trackedFallbackNode;
3811 + const fallbackReplayNode = tracked.fallbackNode;
3812
3813 const children: Array<ReplayNode> = [];
3814 const boundaryNode: void | ReplayNode =
@@ -3853,7 +3861,11 @@ function trackPostpone(
3861 trackedPostpones,
3862 boundary,
3863 );
3856 - if (boundary.trackedContentKeyPath === keyPath && task.childIndex === -1) {
3864 + if (
3865 + boundary.tracked !== null &&
3866 + boundary.tracked.contentKeyPath === keyPath &&
3867 + task.childIndex === -1
3868 + ) {
3869 // Assign ID
3870 if (segment.id === -1) {
3871 if (segment.parentFlushed) {
@@ -3950,7 +3962,11 @@ function untrackBoundary(request: Request, boundary: SuspenseBoundary) {
3962 if (trackedPostpones === null) {
3963 return;
3964 }
3953 - const boundaryKeyPath = boundary.trackedContentKeyPath;
3965 + const tracked = boundary.tracked;
3966 + if (tracked === null) {
3967 + return;
3968 + }
3969 + const boundaryKeyPath = tracked.contentKeyPath;
3970 if (boundaryKeyPath === null) {
3971 return;
3972 }