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) {