From 9938e248feecc6410738cc905744836d6f78c7f8 Mon Sep 17 00:00:00 2001 From: Josh Story Date: Mon, 29 Jul 2024 11:09:54 -0700 Subject: [PATCH] [Fizz] Don't perform work when closing (#30497) When a Fizz render is closing but not yet closed it's possible that pinged tasks can spawn more work. The point of the closing state is to allow time to start piping/reading the underlying stream but semantically the render is finished at that point so work should no longer happen. --- .../src/__tests__/ReactDOMFizzServer-test.js | 61 +++++++++++++++++++ packages/react-server/src/ReactFizzServer.js | 2 +- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index ced32ff87ef12..e00a06abfe2b9 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -3994,6 +3994,67 @@ describe('ReactDOMFizzServer', () => { expect(headers.Link.length).toBe(306); }); + it('does not perform any additional work after fatally erroring', async () => { + let resolve: () => void; + const promise = new Promise(r => { + resolve = r; + }); + function AsyncComp() { + React.use(promise); + return Async; + } + + let didRender = false; + function DidRender({children}) { + didRender = true; + return children; + } + + function ErrorComp() { + throw new Error('boom'); + } + + function App() { + return ( +
+ + + + +
+ ); + } + + let pipe; + const errors = []; + let didFatal = true; + await act(() => { + pipe = renderToPipeableStream(, { + onError(error) { + errors.push(error.message); + }, + onShellError(error) { + didFatal = true; + }, + }).pipe; + }); + + expect(didRender).toBe(false); + await act(() => { + resolve(); + }); + expect(didRender).toBe(false); + + const testWritable = new Stream.Writable(); + await act(() => pipe(testWritable)); + expect(didRender).toBe(false); + expect(didFatal).toBe(didFatal); + expect(errors).toEqual([ + 'boom', + 'The destination stream errored while writing data.', + ]); + }); + describe('error escaping', () => { it('escapes error hash, message, and component stack values in directly flushed errors (html escaping)', async () => { window.__outlet = {}; diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index 85fa3c65d41d5..4d2550758a677 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -4215,7 +4215,7 @@ function retryReplayTask(request: Request, task: ReplayTask): void { } export function performWork(request: Request): void { - if (request.status === CLOSED) { + if (request.status === CLOSED || request.status === CLOSING) { return; } const prevContext = getActiveContext();