Skip to content

Commit 3314293

Browse files
committed
Handle nested SuspenseList
1 parent 5dc1b21 commit 3314293

File tree

2 files changed

+85
-3
lines changed

2 files changed

+85
-3
lines changed

packages/react-dom/src/__tests__/ReactDOMFizzSuspenseList-test.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,4 +324,77 @@ describe('ReactDOMFizSuspenseList', () => {
324324
</div>,
325325
);
326326
});
327+
328+
// @gate enableSuspenseList
329+
it('waits for a nested SuspenseList to complete before resolving "forwards"', async () => {
330+
const A = createAsyncText('A');
331+
const B = createAsyncText('B');
332+
const C = createAsyncText('C');
333+
334+
function Foo() {
335+
return (
336+
<div>
337+
<SuspenseList revealOrder="forwards">
338+
<SuspenseList revealOrder="backwards">
339+
<Suspense fallback={<Text text="Loading A" />}>
340+
<A />
341+
</Suspense>
342+
<Suspense fallback={<Text text="Loading B" />}>
343+
<B />
344+
</Suspense>
345+
</SuspenseList>
346+
<Suspense fallback={<Text text="Loading C" />}>
347+
<C />
348+
</Suspense>
349+
</SuspenseList>
350+
</div>
351+
);
352+
}
353+
354+
await C.resolve();
355+
356+
await serverAct(async () => {
357+
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<Foo />);
358+
pipe(writable);
359+
});
360+
361+
assertLog([
362+
'Suspend! [B]',
363+
'Suspend! [A]',
364+
'C',
365+
'Loading B',
366+
'Loading A',
367+
'Loading C',
368+
]);
369+
370+
expect(getVisibleChildren(container)).toEqual(
371+
<div>
372+
<span>Loading A</span>
373+
<span>Loading B</span>
374+
<span>Loading C</span>
375+
</div>,
376+
);
377+
378+
await serverAct(() => A.resolve());
379+
assertLog(['A']);
380+
381+
expect(getVisibleChildren(container)).toEqual(
382+
<div>
383+
<span>Loading A</span>
384+
<span>Loading B</span>
385+
<span>Loading C</span>
386+
</div>,
387+
);
388+
389+
await serverAct(() => B.resolve());
390+
assertLog(['B']);
391+
392+
expect(getVisibleChildren(container)).toEqual(
393+
<div>
394+
<span>A</span>
395+
<span>B</span>
396+
<span>C</span>
397+
</div>,
398+
);
399+
});
327400
});

packages/react-server/src/ReactFizzServer.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1732,6 +1732,7 @@ function renderSuspenseListRows(
17321732
const prevRow = task.row;
17331733
const totalChildren = rows.length;
17341734

1735+
let previousSuspenseListRow: null | SuspenseListRow = null;
17351736
if (task.replay !== null) {
17361737
// Replay
17371738
// First we need to check if we have any resume slots at this level.
@@ -1763,7 +1764,6 @@ function renderSuspenseListRows(
17631764
}
17641765
}
17651766
} else {
1766-
let previousSuspenseListRow: null | SuspenseListRow = null;
17671767
for (let n = 0; n < totalChildren; n++) {
17681768
// Since we are going to resume into a slot whose order was already
17691769
// determined by the prerender, we can safely resume it even in reverse
@@ -1787,7 +1787,6 @@ function renderSuspenseListRows(
17871787
task = ((task: any): RenderTask); // Refined
17881788
if (revealOrder !== 'backwards') {
17891789
// Forwards direction
1790-
let previousSuspenseListRow: null | SuspenseListRow = null;
17911790
for (let i = 0; i < totalChildren; i++) {
17921791
const node = rows[i];
17931792
if (__DEV__) {
@@ -1809,7 +1808,6 @@ function renderSuspenseListRows(
18091808
const parentSegment = task.blockedSegment;
18101809
const childIndex = parentSegment.children.length;
18111810
const insertionIndex = parentSegment.chunks.length;
1812-
let previousSuspenseListRow: null | SuspenseListRow = null;
18131811
for (let i = totalChildren - 1; i >= 0; i--) {
18141812
const node = rows[i];
18151813
task.row = previousSuspenseListRow = createSuspenseListRow(
@@ -1859,6 +1857,17 @@ function renderSuspenseListRows(
18591857
}
18601858
}
18611859

1860+
if (
1861+
prevRow !== null &&
1862+
previousSuspenseListRow !== null &&
1863+
previousSuspenseListRow.pendingTasks > 0
1864+
) {
1865+
// If we are part of an outer SuspenseList and our last row is still pending, then that blocks
1866+
// the parent row from completing. We can continue the chain.
1867+
prevRow.pendingTasks++;
1868+
previousSuspenseListRow.next = prevRow;
1869+
}
1870+
18621871
// Because this context is always set right before rendering every child, we
18631872
// only need to reset it to the previous value at the very end.
18641873
task.treeContext = prevTreeContext;

0 commit comments

Comments
 (0)