diff --git a/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js b/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js index 0e10207b0bb0..a47e234534db 100644 --- a/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js +++ b/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js @@ -246,6 +246,33 @@ function runActTests(label, render, unmount, rerender) { ]); }); + // @gate __DEV__ + it('warns if a setState is called outside of act(...) after a component threw', () => { + let setValue = null; + function App({defaultValue}) { + if (defaultValue === undefined) { + throw new Error('some error'); + } + const [value, _setValue] = React.useState(defaultValue); + setValue = _setValue; + return value; + } + + expect(() => { + act(() => { + render(, container); + }); + }).toThrow('some error'); + + act(() => { + rerender(, container); + }); + + expect(() => setValue(1)).toErrorDev([ + 'An update to App inside a test was not wrapped in act(...).', + ]); + }); + describe('fake timers', () => { beforeEach(() => { jest.useFakeTimers(); diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index 2db7c319a2ec..cede27655a52 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -779,6 +779,7 @@ function performConcurrentWorkOnRoot(root, didTimeout) { : renderRootSync(root, lanes); if (exitStatus !== RootIncomplete) { if (exitStatus === RootErrored) { + const prevExecutionContext = executionContext; executionContext |= RetryAfterError; // If an error occurred during hydration, @@ -800,6 +801,8 @@ function performConcurrentWorkOnRoot(root, didTimeout) { lanes = errorRetryLanes; exitStatus = renderRootSync(root, errorRetryLanes); } + + executionContext = prevExecutionContext; } if (exitStatus === RootFatalErrored) { @@ -972,6 +975,7 @@ function performSyncWorkOnRoot(root) { let exitStatus = renderRootSync(root, lanes); if (root.tag !== LegacyRoot && exitStatus === RootErrored) { + const prevExecutionContext = executionContext; executionContext |= RetryAfterError; // If an error occurred during hydration, @@ -993,6 +997,8 @@ function performSyncWorkOnRoot(root) { lanes = errorRetryLanes; exitStatus = renderRootSync(root, lanes); } + + executionContext = prevExecutionContext; } if (exitStatus === RootFatalErrored) { diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index 8a93502b905a..0bba7217b1fd 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -779,6 +779,7 @@ function performConcurrentWorkOnRoot(root, didTimeout) { : renderRootSync(root, lanes); if (exitStatus !== RootIncomplete) { if (exitStatus === RootErrored) { + const prevExecutionContext = executionContext; executionContext |= RetryAfterError; // If an error occurred during hydration, @@ -800,6 +801,8 @@ function performConcurrentWorkOnRoot(root, didTimeout) { lanes = errorRetryLanes; exitStatus = renderRootSync(root, errorRetryLanes); } + + executionContext = prevExecutionContext; } if (exitStatus === RootFatalErrored) { @@ -972,6 +975,7 @@ function performSyncWorkOnRoot(root) { let exitStatus = renderRootSync(root, lanes); if (root.tag !== LegacyRoot && exitStatus === RootErrored) { + const prevExecutionContext = executionContext; executionContext |= RetryAfterError; // If an error occurred during hydration, @@ -993,6 +997,8 @@ function performSyncWorkOnRoot(root) { lanes = errorRetryLanes; exitStatus = renderRootSync(root, lanes); } + + executionContext = prevExecutionContext; } if (exitStatus === RootFatalErrored) {