diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js index 4c1b0f7585000..85b452ccc1a06 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js @@ -1046,4 +1046,72 @@ describe('ReactFlightDOM', () => { }); expect(container.innerHTML).toBe('

async hello

'); }); + + // @gate enableUseHook + it('should throw on the client if a passed promise eventually rejects', async () => { + const reportedErrors = []; + const theError = new Error('Server throw'); + + async function getData() { + throw theError; + } + + function Component({data}) { + const text = use(data); + return

{text}

; + } + + const ClientComponent = clientExports(Component); + + function ServerComponent() { + const data = getData(); // no await here + return ; + } + + function Await({response}) { + return use(response); + } + + function App({response}) { + return ( + Loading...}> + ( +

+ {__DEV__ ? e.message + ' + ' : null} + {e.digest} +

+ )}> + +
+
+ ); + } + + const {writable, readable} = getTestStream(); + const {pipe} = ReactServerDOMWriter.renderToPipeableStream( + , + webpackMap, + { + onError(x) { + reportedErrors.push(x); + return __DEV__ ? 'a dev digest' : `digest("${x.message}")`; + }, + }, + ); + pipe(writable); + const response = ReactServerDOMReader.createFromReadableStream(readable); + + const container = document.createElement('div'); + const root = ReactDOMClient.createRoot(container); + await act(async () => { + root.render(); + }); + expect(container.innerHTML).toBe( + __DEV__ + ? '

Server throw + a dev digest

' + : '

digest("Server throw")

', + ); + expect(reportedErrors).toEqual([theError]); + }); }); diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index f610f99b49161..1d5f5b87b6637 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -285,7 +285,8 @@ function serializeThenable(request: Request, thenable: Thenable): number { pingTask(request, newTask); }, reason => { - // TODO: Is it safe to directly emit these without being inside a retry? + newTask.status = ERRORED; + // TODO: We should ideally do this inside performWork so it's scheduled const digest = logRecoverableError(request, reason); if (__DEV__) { const {message, stack} = getErrorMessageAndStackDev(reason); @@ -293,8 +294,9 @@ function serializeThenable(request: Request, thenable: Thenable): number { } else { emitErrorChunkProd(request, newTask.id, digest); } - newTask.status = ERRORED; - pingTask(request, newTask); + if (request.destination !== null) { + flushCompletedChunks(request, request.destination); + } }, );