From 110a01f6acb7f1199730849ecc5a2557c7fc4da4 Mon Sep 17 00:00:00 2001 From: Josh Story Date: Thu, 19 May 2022 08:43:48 -0700 Subject: [PATCH] have abort path run through onError --- .../src/__tests__/ReactDOMFizzServer-test.js | 12 ++++++-- .../ReactDOMFizzServerBrowser-test.js | 22 +++++++++++++- .../__tests__/ReactDOMFizzServerNode-test.js | 30 +++++++++++++++++-- .../ReactDOMServerFB-test.internal.js | 7 ++++- packages/react-server/src/ReactFizzServer.js | 9 ++++-- 5 files changed, 72 insertions(+), 8 deletions(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index d8ac03830b57a..1710161227302 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -1047,9 +1047,15 @@ describe('ReactDOMFizzServer', () => { ); } + const loggedErrors = []; + function onError(error) { + loggedErrors.push(error); + return `Hash of (${error.message})`; + } + let controls; await act(async () => { - controls = ReactDOMFizzServer.renderToPipeableStream(); + controls = ReactDOMFizzServer.renderToPipeableStream(, {onError}); controls.pipe(writable); }); @@ -1077,7 +1083,9 @@ describe('ReactDOMFizzServer', () => { expectErrors( errors, ['This Suspense boundary was aborted by the server'], - ['This Suspense boundary was aborted by the server'], + [ + 'The server could not finish this Suspense boundary, likely due to an error during server rendering. Switched to client rendering.', + ], ); expect(getVisibleChildren(container)).toEqual(
Loading...
); diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServerBrowser-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServerBrowser-test.js index 792b9611f29f2..3aacf68cc3b26 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServerBrowser-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServerBrowser-test.js @@ -193,6 +193,7 @@ describe('ReactDOMFizzServer', () => { // @gate experimental it('should be able to complete by aborting even if the promise never resolves', async () => { + const errors = []; const controller = new AbortController(); const stream = await ReactDOMFizzServer.renderToReadableStream(
@@ -200,13 +201,22 @@ describe('ReactDOMFizzServer', () => {
, - {signal: controller.signal}, + { + signal: controller.signal, + onError(x) { + errors.push(x.message); + }, + }, ); controller.abort(); const result = await readResult(stream); expect(result).toContain('Loading'); + + expect(errors).toEqual([ + 'This Suspense boundary was aborted by the server', + ]); }); // @gate experimental @@ -223,12 +233,18 @@ describe('ReactDOMFizzServer', () => { rendered = true; return 'Done'; } + const errors = []; const stream = await ReactDOMFizzServer.renderToReadableStream(
Loading
}> /> , + { + onError(x) { + errors.push(x.message); + }, + }, ); stream.allReady.then(() => (isComplete = true)); @@ -239,6 +255,10 @@ describe('ReactDOMFizzServer', () => { const reader = stream.getReader(); reader.cancel(); + expect(errors).toEqual([ + 'This Suspense boundary was aborted by the server', + ]); + hasLoaded = true; resolve(); diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServerNode-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServerNode-test.js index c1844d7ef78bf..cb5ec892bd2c8 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServerNode-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServerNode-test.js @@ -211,7 +211,7 @@ describe('ReactDOMFizzServer', () => { { onError(x) { - reportedErrors.push(x); + reportedErrors.push(x.message); }, onShellError(x) { reportedShellErrors.push(x); @@ -224,7 +224,10 @@ describe('ReactDOMFizzServer', () => { expect(output.error).toBe(theError); expect(output.result).toBe(''); - expect(reportedErrors).toEqual([theError]); + expect(reportedErrors).toEqual([ + theError.message, + 'This Suspense boundary was aborted by the server', + ]); expect(reportedShellErrors).toEqual([theError]); }); @@ -289,6 +292,7 @@ describe('ReactDOMFizzServer', () => { // @gate experimental it('should be able to complete by aborting even if the promise never resolves', async () => { let isCompleteCalls = 0; + const errors = []; const {writable, output, completed} = getTestWritable(); const {pipe, abort} = ReactDOMFizzServer.renderToPipeableStream(
@@ -298,6 +302,9 @@ describe('ReactDOMFizzServer', () => {
, { + onError(x) { + errors.push(x.message); + }, onAllReady() { isCompleteCalls++; }, @@ -314,6 +321,9 @@ describe('ReactDOMFizzServer', () => { await completed; + expect(errors).toEqual([ + 'This Suspense boundary was aborted by the server', + ]); expect(output.error).toBe(undefined); expect(output.result).toContain('Loading'); expect(isCompleteCalls).toBe(1); @@ -322,6 +332,7 @@ describe('ReactDOMFizzServer', () => { // @gate experimental it('should be able to complete by abort when the fallback is also suspended', async () => { let isCompleteCalls = 0; + const errors = []; const {writable, output, completed} = getTestWritable(); const {pipe, abort} = ReactDOMFizzServer.renderToPipeableStream(
@@ -333,6 +344,9 @@ describe('ReactDOMFizzServer', () => {
, { + onError(x) { + errors.push(x.message); + }, onAllReady() { isCompleteCalls++; }, @@ -349,6 +363,11 @@ describe('ReactDOMFizzServer', () => { await completed; + expect(errors).toEqual([ + // There are two boundaries that abort + 'This Suspense boundary was aborted by the server', + 'This Suspense boundary was aborted by the server', + ]); expect(output.error).toBe(undefined); expect(output.result).toContain('Loading'); expect(isCompleteCalls).toBe(1); @@ -552,6 +571,7 @@ describe('ReactDOMFizzServer', () => { rendered = true; return 'Done'; } + const errors = []; const {writable, completed} = getTestWritable(); const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
@@ -560,6 +580,9 @@ describe('ReactDOMFizzServer', () => {
, { + onError(x) { + errors.push(x.message); + }, onAllReady() { isComplete = true; }, @@ -579,6 +602,9 @@ describe('ReactDOMFizzServer', () => { await completed; + expect(errors).toEqual([ + 'This Suspense boundary was aborted by the server', + ]); expect(rendered).toBe(false); expect(isComplete).toBe(true); }); diff --git a/packages/react-server-dom-relay/src/__tests__/ReactDOMServerFB-test.internal.js b/packages/react-server-dom-relay/src/__tests__/ReactDOMServerFB-test.internal.js index 7444ae6f90934..1550ca0d3cfa7 100644 --- a/packages/react-server-dom-relay/src/__tests__/ReactDOMServerFB-test.internal.js +++ b/packages/react-server-dom-relay/src/__tests__/ReactDOMServerFB-test.internal.js @@ -171,6 +171,7 @@ describe('ReactDOMServerFB', () => { }); it('should be able to complete by aborting even if the promise never resolves', () => { + const errors = []; const stream = ReactDOMServer.renderToStream(
Loading
}> @@ -179,7 +180,7 @@ describe('ReactDOMServerFB', () => { , { onError(x) { - console.error(x); + errors.push(x.message); }, }, ); @@ -191,5 +192,9 @@ describe('ReactDOMServerFB', () => { const remaining = readResult(stream); expect(remaining).toEqual(''); + + expect(errors).toEqual([ + 'This Suspense boundary was aborted by the server', + ]); }); }); diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index 7bea87aafb4ad..53519ec897353 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -1499,8 +1499,13 @@ function abortTask(task: Task): void { if (!boundary.forceClientRender) { boundary.forceClientRender = true; - boundary.errorMessage = - 'This Suspense boundary was aborted by the server'; + const error = new Error( + 'This Suspense boundary was aborted by the server', + ); + boundary.errorHash = request.onError(error); + if (__DEV__) { + captureBoundaryErrorDetailsDev(boundary, error); + } if (boundary.parentFlushed) { request.clientRenderedBoundaries.push(boundary); }