Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 40 additions & 4 deletions packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4003,10 +4003,46 @@ describe('ReactDOMFizzServer', () => {
await act(() => pipe(testWritable));
expect(didRender).toBe(false);
expect(didFatal).toBe(didFatal);
expect(errors).toEqual([
'boom',
'The destination stream errored while writing data.',
]);
expect(errors).toEqual(['boom']);
});

it('does not report aborts after fatally erroring', async () => {
const promise = new Promise(() => {});
function AsyncComp() {
React.use(promise);
return 'Async';
}

function ErrorComp() {
throw new Error('boom');
}

const errors = [];
let abort;
await act(() => {
abort = renderToPipeableStream(
<div>
<Suspense fallback="loading...">
<AsyncComp />
</Suspense>
<ErrorComp />
</div>,
{
onError(error) {
errors.push(error.message);
},
onShellError() {},
},
).abort;
});

expect(errors).toEqual(['boom']);

await act(() => {
abort(new Error('too late'));
});

expect(errors).toEqual(['boom']);
});

describe('error escaping', () => {
Expand Down
36 changes: 32 additions & 4 deletions packages/react-dom/src/__tests__/ReactDOMFizzServerNode-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,37 @@ describe('ReactDOMFizzServerNode', () => {
expect(reportedShellErrors).toEqual([theError]);
});

it('should not report aborts after the shell has fatally errored', async () => {
const reportedErrors = [];
const reportedShellErrors = [];
const {abort} = ReactDOMFizzServer.renderToPipeableStream(
<div>
<Suspense fallback="Loading">
<InfiniteSuspend />
</Suspense>
<Throw />
</div>,
{
onError(x) {
reportedErrors.push(x);
},
onShellError(x) {
reportedShellErrors.push(x);
},
},
);

await jest.runAllTimers();

expect(reportedErrors).toEqual([theError]);
expect(reportedShellErrors).toEqual([theError]);

abort(new Error('too late'));

expect(reportedErrors).toEqual([theError]);
expect(reportedShellErrors).toEqual([theError]);
});

it('should error the stream when an error is thrown inside a fallback', async () => {
const reportedErrors = [];
const reportedShellErrors = [];
Expand All @@ -263,10 +294,7 @@ describe('ReactDOMFizzServerNode', () => {

expect(output.error).toBe(theError);
expect(output.result).toBe('');
expect(reportedErrors).toEqual([
theError.message,
'The destination stream errored while writing data.',
]);
expect(reportedErrors).toEqual([theError.message]);
expect(reportedShellErrors).toEqual([theError]);
});

Expand Down
7 changes: 5 additions & 2 deletions packages/react-server/src/ReactFizzServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6143,9 +6143,12 @@ export function stopFlowing(request: Request): void {

// This is called to early terminate a request. It puts all pending boundaries in client rendered state.
export function abort(request: Request, reason: mixed): void {
if (request.status === OPEN || request.status === OPENING) {
request.status = ABORTING;
if (request.status !== OPEN && request.status !== OPENING) {
// Only requests that are not already complete or in the process of aborting
// can be aborted. in practice this makes abort callable at most once per render.
return;
}
request.status = ABORTING;

try {
const abortableTasks = request.abortableTasks;
Expand Down
Loading