@samitouri / QOS-React-2 / commits / 463b808176

[Fizz] Reset the segent id assignment when postponing the root (#33755)

When postponing the root we encode the segment Id into the postponed state but we should really be reseting it to zero so we can restart the counter from the beginning when the resume is actually just a re-render. This also no longer assigns the root segment id based on the postponed state when resuming the root for the same reason. In the future we may use the embedded replay segment id if we implement resuming the root without re-rendering everything but that is not yet implemented or planned.

Josh Story committed Jul 10, 2025 at 12:12 UTC 463b808176ad7c9429a4981bb45a1da225fd4b85
2 files changed +12 -9
packages/react-dom/src/__tests__/ReactDOMFizzStaticBrowser-test.js
+3 -3
@@ -1623,7 +1623,7 @@ describe('ReactDOMFizzStaticBrowser', () => {
1623
1624 expect(result).toBe(
1625 '<!DOCTYPE html><html><head><link rel="expect" href="#_R_" blocking="render"/></head>' +
1626 - '<body>hello<!--$?--><template id="B:1"></template><!--/$--><script id="_R_">requestAnimationFrame(function(){$RT=performance.now()});</script>',
1626 + '<body>hello<!--$?--><template id="B:0"></template><!--/$--><script id="_R_">requestAnimationFrame(function(){$RT=performance.now()});</script>',
1627 );
1628
1629 await 1;
@@ -1648,8 +1648,8 @@ describe('ReactDOMFizzStaticBrowser', () => {
1648
1649 expect(slice).toBe(
1650 '<!DOCTYPE html><html><head><link rel="expect" href="#_R_" blocking="render"/></head>' +
1651 - '<body>hello<!--$?--><template id="B:1"></template><!--/$--><script id="_R_">requestAnimationFrame(function(){$RT=performance.now()});</script>' +
1652 - '<div hidden id="S:1">world<!-- --></div><script>$RX',
1651 + '<body>hello<!--$?--><template id="B:0"></template><!--/$--><script id="_R_">requestAnimationFrame(function(){$RT=performance.now()});</script>' +
1652 + '<div hidden id="S:0">world<!-- --></div><script>$RX',
1653 );
1654 });
1655
packages/react-server/src/ReactFizzServer.js
+9 -6
@@ -666,7 +666,6 @@ export function resumeRequest(
666 request.nextSegmentId = postponedState.nextSegmentId;
667
668 if (typeof postponedState.replaySlots === 'number') {
669 - const resumedId = postponedState.replaySlots;
669 // We have a resume slot at the very root. This is effectively just a full rerender.
670 const rootSegment = createPendingSegment(
671 request,
@@ -677,7 +676,6 @@ export function resumeRequest(
676 false,
677 false,
678 );
680 - rootSegment.id = resumedId;
679 // There is no parent so conceptually, we're unblocked to flush this segment.
680 rootSegment.parentFlushed = true;
681 const rootTask = createRenderTask(
@@ -6326,6 +6324,7 @@ export function getPostponedState(request: Request): null | PostponedState {
6324 return null;
6325 }
6326 let replaySlots: ResumeSlots;
6327 + let nextSegmentId: number;
6328 if (
6329 request.completedRootSegment !== null &&
6330 // The Root postponed
@@ -6333,17 +6332,21 @@ export function getPostponedState(request: Request): null | PostponedState {
6332 // Or the Preamble was not available
6333 request.completedPreambleSegments === null)
6334 ) {
6336 - // This is necessary for the pending preamble case and is idempotent for the
6337 - // postponed root case
6338 - replaySlots = request.completedRootSegment.id;
6335 + nextSegmentId = 0;
6336 + // We need to ensure that on resume we retry the root. We use a number
6337 + // type for the replaySlots to signify this (see resumeRequest).
6338 + // The value -1 represents an unassigned ID but is not functionally meaningful
6339 + // for resuming at the root.
6340 + replaySlots = -1;
6341 // We either postponed the root or we did not have a preamble to flush
6342 resetResumableState(request.resumableState, request.renderState);
6343 } else {
6344 + nextSegmentId = request.nextSegmentId;
6345 replaySlots = trackedPostpones.rootSlots;
6346 completeResumableState(request.resumableState);
6347 }
6348 return {
6346 - nextSegmentId: request.nextSegmentId,
6349 + nextSegmentId,
6350 rootFormatContext: request.rootFormatContext,
6351 progressiveChunkSize: request.progressiveChunkSize,
6352 resumableState: request.resumableState,