diff --git a/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js b/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js index 3900a76c92cf4..8602273cf12d7 100644 --- a/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js +++ b/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js @@ -3717,7 +3717,7 @@ export function writeEndSegment( } const completeSegmentScript1Full = stringToPrecomputedChunk( - completeSegmentFunction + ';$RS("', + completeSegmentFunction + '$RS("', ); const completeSegmentScript1Partial = stringToPrecomputedChunk('$RS("'); const completeSegmentScript2 = stringToPrecomputedChunk('","'); diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index a26632bbd01be..4d035062bfa25 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -169,36 +169,40 @@ const REPLAY_SUSPENSE_BOUNDARY = 1; const RESUME_ELEMENT = 2; const RESUME_SLOT = 3; -type ResumableParentNode = +type ReplaySuspenseBoundary = [ + 1, // REPLAY_SUSPENSE_BOUNDARY + string | null /* name */, + string | number /* key */, + Array /* children */, + SuspenseBoundaryID /* id */, +]; + +type ReplayNode = | [ 0, // REPLAY_NODE string | null /* name */, string | number /* key */, Array /* children */, ] - | [ - 1, // REPLAY_SUSPENSE_BOUNDARY - string | null /* name */, - string | number /* key */, - Array /* children */, - SuspenseBoundaryID, - ]; -type ResumableNode = - | ResumableParentNode - | [ - 2, // RESUME_ELEMENT - string | null /* name */, - string | number /* key */, - number /* segment id */, - ] - | [ - 3, // RESUME_SLOT - number /* index */, - number /* segment id */, - ]; + | ReplaySuspenseBoundary; + +type ResumeElement = [ + 2, // RESUME_ELEMENT + string | null /* name */, + string | number /* key */, + number /* segment id */, +]; + +type ResumeSlot = [ + 3, // RESUME_SLOT + number /* index */, + number /* segment id */, +]; + +type ResumableNode = ReplayNode | ResumeElement | ResumeSlot; type PostponedHoles = { - workingMap: Map, + workingMap: Map, root: Array, }; @@ -394,6 +398,7 @@ export function createRequest( request, null, children, + -1, null, rootSegment, abortSet, @@ -494,6 +499,7 @@ export function resumeRequest( request, null, children, + -1, null, rootSegment, abortSet, @@ -551,6 +557,7 @@ function createTask( request: Request, thenableState: ThenableState | null, node: ReactNodeList, + childIndex: number, blockedBoundary: Root | SuspenseBoundary, blockedSegment: Segment, abortSet: Set, @@ -568,6 +575,7 @@ function createTask( } const task: Task = ({ node, + childIndex, ping: () => pingTask(request, task), blockedBoundary, blockedSegment, @@ -578,7 +586,6 @@ function createTask( context, treeContext, thenableState, - childIndex: -1, }: any); if (__DEV__) { task.componentStack = null; @@ -730,6 +737,8 @@ function renderSuspenseBoundary( props: Object, ): void { pushBuiltInComponentStackInDEV(task, 'Suspense'); + + const prevKeyPath = task.keyPath; const parentBoundary = task.blockedBoundary; const parentSegment = task.blockedSegment; @@ -791,6 +800,7 @@ function renderSuspenseBoundary( newBoundary.resources, ); } + task.keyPath = keyPath; try { // We use the safe form because we don't handle suspending here. Only error handling. renderNode(request, task, content, -1); @@ -844,6 +854,7 @@ function renderSuspenseBoundary( } task.blockedBoundary = parentBoundary; task.blockedSegment = parentSegment; + task.keyPath = prevKeyPath; } // We create suspended task for the fallback because we don't want to actually work @@ -852,6 +863,7 @@ function renderSuspenseBoundary( request, null, fallback, + -1, parentBoundary, boundarySegment, fallbackAbortSet, @@ -1938,7 +1950,7 @@ function trackPostpone( ); } const children: Array = []; - const boundaryNode: ResumableParentNode = [ + const boundaryNode: ReplaySuspenseBoundary = [ REPLAY_SUSPENSE_BOUNDARY, boundaryKeyPath[1], boundaryKeyPath[2], @@ -1946,7 +1958,7 @@ function trackPostpone( boundary.id, ]; trackedPostpones.workingMap.set(boundaryKeyPath, boundaryNode); - addToResumableParent(boundaryNode, boundaryKeyPath[0], trackedPostpones); + addToReplayParent(boundaryNode, boundaryKeyPath[0], trackedPostpones); } const keyPath = task.keyPath; @@ -1964,11 +1976,11 @@ function trackPostpone( keyPath[2], segment.id, ]; - addToResumableParent(resumableElement, keyPath[0], trackedPostpones); + addToReplayParent(resumableElement, keyPath[0], trackedPostpones); } else { // Resume at the slot within the array const resumableNode = [RESUME_SLOT, task.childIndex, segment.id]; - addToResumableParent(resumableNode, keyPath, trackedPostpones); + addToReplayParent(resumableNode, keyPath, trackedPostpones); } } @@ -2023,6 +2035,7 @@ function spawnNewSuspendedTask( request, thenableState, task.node, + task.childIndex, task.blockedBoundary, newSegment, task.abortSet, @@ -2032,7 +2045,6 @@ function spawnNewSuspendedTask( task.context, task.treeContext, ); - newTask.childIndex = task.childIndex; if (__DEV__) { if (task.componentStack !== null) { @@ -3072,7 +3084,7 @@ export function getResumableState(request: Request): ResumableState { return request.resumableState; } -function addToResumableParent( +function addToReplayParent( node: ResumableNode, parentKeyPath: Root | KeyNode, trackedPostpones: PostponedHoles, @@ -3088,9 +3100,9 @@ function addToResumableParent( parentKeyPath[1], parentKeyPath[2], ([]: Array), - ]: ResumableParentNode); + ]: ReplayNode); workingMap.set(parentKeyPath, parentNode); - addToResumableParent(parentNode, parentKeyPath[0], trackedPostpones); + addToReplayParent(parentNode, parentKeyPath[0], trackedPostpones); } parentNode[3].push(node); }