@samitouri / QOS-React / commits / 9090257e6e

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