diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index 13774ec2de07..a42056ab2492 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -796,6 +796,73 @@ describe('ReactDOMFizzServer', () => { ); }); + // @gate experimental + it('should recover the outer context when an error happens inside a provider', async () => { + const ContextA = React.createContext('A0'); + const ContextB = React.createContext('B0'); + + function PrintA() { + return ( + {value => } + ); + } + + class PrintB extends React.Component { + static contextType = ContextB; + render() { + return ; + } + } + + function Throws() { + const value = React.useContext(ContextA); + throw new Error(value); + } + + const loggedErrors = []; + await act(async () => { + const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable( +
+ +
+ + + + + }> + + + + + + +
+ +
, + writable, + { + onError(x) { + loggedErrors.push(x); + }, + }, + ); + startWriting(); + }); + expect(loggedErrors.length).toBe(1); + expect(loggedErrors[0].message).toEqual('A0.1.1'); + expect(getVisibleChildren(container)).toEqual( +
+ A0 +
+ Loading...B0 +
+ A0 +
, + ); + }); + // @gate experimental it('client renders a boundary if it errors before finishing the fallback', async () => { function App({isClient}) { diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index 9409b57bbbf1..d51ad44ed64e 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -1174,6 +1174,13 @@ function renderNode(request: Request, task: Task, node: ReactNodeList): void { // Restore all active ReactContexts to what they were before. switchContext(previousContext); } else { + // Restore the context. We assume that this will be restored by the inner + // functions in case nothing throws so we don't use "finally" here. + task.blockedSegment.formatContext = previousFormatContext; + task.legacyContext = previousLegacyContext; + task.context = previousContext; + // Restore all active ReactContexts to what they were before. + switchContext(previousContext); // We assume that we don't need the correct context. // Let's terminate the rest of the tree and don't render any siblings. throw x;