diff --git a/packages/react-dom/src/__tests__/ReactDOMRoot-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMRoot-test.internal.js index 83633532dca4a..cd00c561b6320 100644 --- a/packages/react-dom/src/__tests__/ReactDOMRoot-test.internal.js +++ b/packages/react-dom/src/__tests__/ReactDOMRoot-test.internal.js @@ -320,4 +320,14 @@ describe('ReactDOMRoot', () => { flush(); expect(container.textContent).toEqual('1'); }); + + it('handles fatal errors triggered by batch.commit()', () => { + const root = ReactDOM.createRoot(container); + const batch = root.createBatch(); + const InvalidType = undefined; + expect(() => batch.render()).toWarnDev([ + 'React.createElement: type is invalid', + ]); + expect(() => batch.commit()).toThrow('Element type is invalid'); + }); }); diff --git a/packages/react-reconciler/src/ReactFiberScheduler.js b/packages/react-reconciler/src/ReactFiberScheduler.js index ec4c554182eab..716f963b9df0b 100644 --- a/packages/react-reconciler/src/ReactFiberScheduler.js +++ b/packages/react-reconciler/src/ReactFiberScheduler.js @@ -884,7 +884,12 @@ export default function( const sourceFiber: Fiber = nextUnitOfWork; let returnFiber = sourceFiber.return; if (returnFiber === null) { - // This is a fatal error. + // This is the root. The root could capture its own errors. However, + // we don't know if it errors before or after we pushed the host + // context. This information is needed to avoid a stack mismatch. + // Because we're not sure, treat this as a fatal error. We could track + // which phase it fails in, but doesn't seem worth it. At least + // for now. didFatal = true; onUncaughtError(thrownValue); break; @@ -1444,6 +1449,8 @@ export default function( // Perform work on root as if the given expiration time is the current time. // This has the effect of synchronously flushing all work up to and // including the given time. + nextFlushedRoot = root; + nextFlushedExpirationTime = expirationTime; performWorkOnRoot(root, expirationTime, false); finishRendering(); }