@samitouri / QOS-React-2 / commits / 69728fde0a

[Flight] Dedupe suspense boundaries when it has already been found earlier (#27408)

If we track a postponed SuspenseBoundary parent that we have to replay through before it has postponed and it postpones itself later, we need to upgrade it to a postponed replay boundary instead of adding two.

Sebastian Markbåge committed Sep 23, 2023 at 12:33 UTC 69728fde0ab2822ba468d9aaaa0c856de3f8547d
2 files changed +64 -9
packages/react-dom/src/__tests__/ReactDOMFizzStaticBrowser-test.js
+47
@@ -840,4 +840,51 @@ describe('ReactDOMFizzStaticBrowser', () => {
840 </div>,
841 ]);
842 });
843 +
844 + // @gate enablePostpone
845 + it('can postpone a boundary after it has already been added', async () => {
846 + let prerendering = true;
847 + function Postpone() {
848 + if (prerendering) {
849 + React.unstable_postpone();
850 + }
851 + return 'Hello';
852 + }
853 +
854 + function App() {
855 + return (
856 + <div>
857 + <Suspense fallback="Loading...">
858 + <Suspense fallback="Loading...">
859 + <Postpone />
860 + </Suspense>
861 + <Postpone />
862 + <Postpone />
863 + </Suspense>
864 + </div>
865 + );
866 + }
867 +
868 + const prerendered = await ReactDOMFizzStatic.prerender(<App />);
869 + expect(prerendered.postponed).not.toBe(null);
870 +
871 + prerendering = false;
872 +
873 + console.log(JSON.stringify(prerendered.postponed, null, 2));
874 +
875 + const resumed = await ReactDOMFizzServer.resume(
876 + <App />,
877 + JSON.parse(JSON.stringify(prerendered.postponed)),
878 + );
879 +
880 + await readIntoContainer(prerendered.prelude);
881 +
882 + expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
883 +
884 + await readIntoContainer(resumed);
885 +
886 + expect(getVisibleChildren(container)).toEqual(
887 + <div>{['Hello', 'Hello', 'Hello']}</div>,
888 + );
889 + });
890 });
packages/react-server/src/ReactFizzServer.js
+17 -9
@@ -2490,15 +2490,23 @@ function trackPostpone(
2490 addToReplayParent(boundaryNode, boundaryKeyPath[0], trackedPostpones);
2491 return;
2492 } else {
2493 - const boundaryNode: ReplaySuspenseBoundary = [
2494 - boundaryKeyPath[1],
2495 - boundaryKeyPath[2],
2496 - children,
2497 - null,
2498 - boundary.rootSegmentID,
2499 - ];
2500 - trackedPostpones.workingMap.set(boundaryKeyPath, boundaryNode);
2501 - addToReplayParent(boundaryNode, boundaryKeyPath[0], trackedPostpones);
2493 + let boundaryNode: void | ReplayNode =
2494 + trackedPostpones.workingMap.get(boundaryKeyPath);
2495 + if (boundaryNode === undefined) {
2496 + boundaryNode = [
2497 + boundaryKeyPath[1],
2498 + boundaryKeyPath[2],
2499 + children,
2500 + null,
2501 + boundary.rootSegmentID,
2502 + ];
2503 + trackedPostpones.workingMap.set(boundaryKeyPath, boundaryNode);
2504 + addToReplayParent(boundaryNode, boundaryKeyPath[0], trackedPostpones);
2505 + } else {
2506 + // Upgrade to ReplaySuspenseBoundary.
2507 + ((boundaryNode: any): ReplaySuspenseBoundary)[4] =
2508 + boundary.rootSegmentID;
2509 + }
2510 // Fall through to add the child node.
2511 }
2512 }