fix: restore execution context after RetryAfterError completed (#21766)
* test: Add failing test due to executionContext not being restored * fix: restore execution context after RetryAfterError completed * Poke codesandbox/ci * Completely restore executionContext * expect a specific error
Sebastian Silbermann committed
Jul 13, 2021 at 23:38 UTC
9090257e6edec44a09a4cd5723d1d0f7ce2ab7c4
3 files changed
+39
packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js
+27
@@ -246,6 +246,33 @@ function runActTests(label, render, unmount, rerender) {
246
]);
247
});
248
249
+ // @gate __DEV__
250
+ it('warns if a setState is called outside of act(...) after a component threw', () => {
251
+ let setValue = null;
252
+ function App({defaultValue}) {
253
+ if (defaultValue === undefined) {
254
+ throw new Error('some error');
255
+ }
256
+ const [value, _setValue] = React.useState(defaultValue);
257
+ setValue = _setValue;
258
+ return value;
259
+ }
260
+
261
+ expect(() => {
262
+ act(() => {
263
+ render(<App defaultValue={undefined} />, container);
264
+ });
265
+ }).toThrow('some error');
266
+
267
+ act(() => {
268
+ rerender(<App defaultValue={0} />, container);
269
+ });
270
+
271
+ expect(() => setValue(1)).toErrorDev([
272
+ 'An update to App inside a test was not wrapped in act(...).',
273
+ ]);
274
+ });
275
+
276
describe('fake timers', () => {
277
beforeEach(() => {
278
jest.useFakeTimers();
packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+6
@@ -779,6 +779,7 @@ function performConcurrentWorkOnRoot(root, didTimeout) {
779
: renderRootSync(root, lanes);
780
if (exitStatus !== RootIncomplete) {
781
if (exitStatus === RootErrored) {
782
+ const prevExecutionContext = executionContext;
783
executionContext |= RetryAfterError;
784
785
// If an error occurred during hydration,
@@ -800,6 +801,8 @@ function performConcurrentWorkOnRoot(root, didTimeout) {
801
lanes = errorRetryLanes;
802
exitStatus = renderRootSync(root, errorRetryLanes);
803
}
804
+
805
+ executionContext = prevExecutionContext;
806
}
807
808
if (exitStatus === RootFatalErrored) {
@@ -972,6 +975,7 @@ function performSyncWorkOnRoot(root) {
975
976
let exitStatus = renderRootSync(root, lanes);
977
if (root.tag !== LegacyRoot && exitStatus === RootErrored) {
978
+ const prevExecutionContext = executionContext;
979
executionContext |= RetryAfterError;
980
981
// If an error occurred during hydration,
@@ -993,6 +997,8 @@ function performSyncWorkOnRoot(root) {
997
lanes = errorRetryLanes;
998
exitStatus = renderRootSync(root, lanes);
999
}
1000
+
1001
+ executionContext = prevExecutionContext;
1002
}
1003
1004
if (exitStatus === RootFatalErrored) {
packages/react-reconciler/src/ReactFiberWorkLoop.old.js
+6
@@ -779,6 +779,7 @@ function performConcurrentWorkOnRoot(root, didTimeout) {
779
: renderRootSync(root, lanes);
780
if (exitStatus !== RootIncomplete) {
781
if (exitStatus === RootErrored) {
782
+ const prevExecutionContext = executionContext;
783
executionContext |= RetryAfterError;
784
785
// If an error occurred during hydration,
@@ -800,6 +801,8 @@ function performConcurrentWorkOnRoot(root, didTimeout) {
801
lanes = errorRetryLanes;
802
exitStatus = renderRootSync(root, errorRetryLanes);
803
}
804
+
805
+ executionContext = prevExecutionContext;
806
}
807
808
if (exitStatus === RootFatalErrored) {
@@ -972,6 +975,7 @@ function performSyncWorkOnRoot(root) {
975
976
let exitStatus = renderRootSync(root, lanes);
977
if (root.tag !== LegacyRoot && exitStatus === RootErrored) {
978
+ const prevExecutionContext = executionContext;
979
executionContext |= RetryAfterError;
980
981
// If an error occurred during hydration,
@@ -993,6 +997,8 @@ function performSyncWorkOnRoot(root) {
997
lanes = errorRetryLanes;
998
exitStatus = renderRootSync(root, lanes);
999
}
1000
+
1001
+ executionContext = prevExecutionContext;
1002
}
1003
1004
if (exitStatus === RootFatalErrored) {