Skip to content

Commit 88098cc

Browse files
committed
Unblock a boundary blocking a row if the boundary errors
1 parent 716114d commit 88098cc

File tree

2 files changed

+78
-0
lines changed

2 files changed

+78
-0
lines changed

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

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,12 @@ describe('ReactDOMFizzSuspenseList', () => {
108108

109109
function createAsyncText(text) {
110110
let resolved = false;
111+
let error = undefined;
111112
const Component = function () {
113+
if (error !== undefined) {
114+
Scheduler.log('Error! [' + error.message + ']');
115+
throw error;
116+
}
112117
if (!resolved) {
113118
Scheduler.log('Suspend! [' + text + ']');
114119
throw promise;
@@ -120,6 +125,10 @@ describe('ReactDOMFizzSuspenseList', () => {
120125
resolved = true;
121126
return resolve();
122127
};
128+
Component.reject = function (e) {
129+
error = e;
130+
return resolve();
131+
};
123132
});
124133
return Component;
125134
}
@@ -774,4 +783,65 @@ describe('ReactDOMFizzSuspenseList', () => {
774783
'The render was aborted by the server without a reason.',
775784
]);
776785
});
786+
787+
// @gate enableSuspenseList
788+
it('can error a pending SuspenseList', async () => {
789+
const A = createAsyncText('A');
790+
791+
function Foo() {
792+
return (
793+
<div>
794+
<SuspenseList revealOrder="forwards">
795+
<Suspense fallback={<Text text="Loading A" />}>
796+
<A />
797+
</Suspense>
798+
<Suspense fallback={<Text text="Loading B" />}>
799+
<Text text="B" />
800+
</Suspense>
801+
</SuspenseList>
802+
</div>
803+
);
804+
}
805+
806+
const errors = [];
807+
await serverAct(async () => {
808+
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<Foo />, {
809+
onError(error) {
810+
errors.push(error.message);
811+
},
812+
});
813+
pipe(writable);
814+
});
815+
816+
assertLog([
817+
'Suspend! [A]',
818+
'B', // TODO: Defer rendering the content after fallback if previous suspended,
819+
'Loading A',
820+
'Loading B',
821+
]);
822+
823+
expect(getVisibleChildren(container)).toEqual(
824+
<div>
825+
<span>Loading A</span>
826+
<span>Loading B</span>
827+
</div>,
828+
);
829+
830+
await serverAct(async () => {
831+
A.reject(new Error('hi'));
832+
});
833+
834+
assertLog(['Error! [hi]']);
835+
836+
expect(getVisibleChildren(container)).toEqual(
837+
<div>
838+
<span>Loading A</span>
839+
<span>B</span>
840+
</div>,
841+
);
842+
843+
expect(errors).toEqual(['hi']);
844+
expect(hasErrored).toBe(false);
845+
expect(hasCompleted).toBe(true);
846+
});
777847
});

packages/react-server/src/ReactFizzServer.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4392,6 +4392,14 @@ function erroredTask(
43924392
encodeErrorForBoundary(boundary, errorDigest, error, errorInfo, false);
43934393
untrackBoundary(request, boundary);
43944394

4395+
const boundaryRow = boundary.row;
4396+
if (boundaryRow !== null) {
4397+
// Unblock the SuspenseListRow that was blocked by this boundary.
4398+
if (--boundaryRow.pendingTasks === 0) {
4399+
finishSuspenseListRow(request, boundaryRow);
4400+
}
4401+
}
4402+
43954403
// Regardless of what happens next, this boundary won't be displayed,
43964404
// so we can flush it, if the parent already flushed.
43974405
if (boundary.parentFlushed) {

0 commit comments

Comments
 (0)