diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index e9abb25eb1768..5d70a0c71ea4d 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -8377,6 +8377,48 @@ describe('ReactDOMFizzServer', () => { ); }); + it('can support throwing after aborting during a render', async () => { + function App() { + return ( +
+ loading...

}> + +
+
+ ); + } + + function ComponentThatAborts() { + abortRef.current('boom'); + throw new Error('bam'); + } + + const abortRef = {current: null}; + let finished = false; + const errors = []; + await act(() => { + const {pipe, abort} = renderToPipeableStream(, { + onError(err) { + errors.push(err); + }, + }); + abortRef.current = abort; + writable.on('finish', () => { + finished = true; + }); + pipe(writable); + }); + + expect(errors).toEqual(['boom']); + + expect(finished).toBe(true); + expect(getVisibleChildren(container)).toEqual( +
+

loading...

+
, + ); + }); + it('should warn for using generators as children props', async () => { function* getChildren() { yield

Hello

; diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index a6c77f8bafb7f..986e8673a5a2a 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -652,8 +652,6 @@ export function resumeRequest( return request; } -const AbortSigil = {}; - let currentRequest: null | Request = null; export function resolveRequest(): null | Request { @@ -1173,7 +1171,7 @@ function renderSuspenseBoundary( ); boundarySegment.status = COMPLETED; } catch (thrownValue: mixed) { - if (thrownValue === AbortSigil) { + if (request.status === ABORTING) { boundarySegment.status = ABORTED; } else { boundarySegment.status = ERRORED; @@ -1246,7 +1244,7 @@ function renderSuspenseBoundary( } catch (thrownValue: mixed) { newBoundary.status = CLIENT_RENDERED; let error: mixed; - if (thrownValue === AbortSigil) { + if (request.status === ABORTING) { contentRootSegment.status = ABORTED; error = request.fatalError; } else { @@ -1601,7 +1599,8 @@ function finishClassComponent( nextChildren = instance.render(); } if (request.status === ABORTING) { - throw AbortSigil; + // eslint-disable-next-line no-throw-literal + throw null; } if (__DEV__) { @@ -1757,7 +1756,8 @@ function renderFunctionComponent( legacyContext, ); if (request.status === ABORTING) { - throw AbortSigil; + // eslint-disable-next-line no-throw-literal + throw null; } const hasId = checkDidRenderIdHook(); @@ -2076,7 +2076,8 @@ function renderLazyComponent( Component = init(payload); } if (request.status === ABORTING) { - throw AbortSigil; + // eslint-disable-next-line no-throw-literal + throw null; } const resolvedProps = resolveDefaultPropsOnNonClassComponent( Component, @@ -2655,7 +2656,8 @@ function retryNode(request: Request, task: Task): void { resolvedNode = init(payload); } if (request.status === ABORTING) { - throw AbortSigil; + // eslint-disable-next-line no-throw-literal + throw null; } // Now we render the resolved node renderNodeDestructive(request, task, resolvedNode, childIndex); @@ -4127,7 +4129,7 @@ function retryRenderTask( // (unstable) API for suspending. This implementation detail can change // later, once we deprecate the old API in favor of `use`. getSuspendedThenable() - : thrownValue === AbortSigil + : request.status === ABORTING ? request.fatalError : thrownValue; @@ -4250,7 +4252,7 @@ function retryReplayTask(request: Request, task: ReplayTask): void { erroredReplay( request, task.blockedBoundary, - x === AbortSigil ? request.fatalError : x, + request.status === ABORTING ? request.fatalError : x, errorInfo, task.replay.nodes, task.replay.slots,