From c21ed5a3ca43d588cf925fb1ce5cfed28770715a Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 28 Mar 2018 18:02:01 -0700 Subject: [PATCH] Fix bug when fatal error is thrown as a result of `batch.commit` Fixes #12474 --- .../src/__tests__/ReactDOMRoot-test.internal.js | 10 ++++++++++ packages/react-reconciler/src/ReactFiberScheduler.js | 9 ++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) 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(); }