Skip to content

Commit a1dbb85

Browse files
threepointoneacdlite
authored andcommitted
warn if you try to use act() in prod (#16282)
We have behaviour divergence for act() between prod and dev (specifically, act() + concurrent mode does not flush fallbacks in prod. This doesn't affect anyone in OSS yet) We also don't have a good story for writing tests in prod (and what from what I gather, nobody really writes tests in prod mode). We could have wiped out act() in prod builds, except that _we_ ourselves use act() for our tests when we run them in prod mode. This PR is a compromise to all of this. We will log a warning if you try to use act() in prod mode, and we silence it in our test suites.
1 parent dc232e6 commit a1dbb85

File tree

5 files changed

+60
-2
lines changed

5 files changed

+60
-2
lines changed

packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ function sleep(period) {
2727

2828
describe('ReactTestUtils.act()', () => {
2929
// first we run all the tests with concurrent mode
30-
let concurrentRoot;
30+
let concurrentRoot = null;
3131
function renderConcurrent(el, dom) {
3232
concurrentRoot = ReactDOM.unstable_createRoot(dom);
3333
concurrentRoot.render(el);
@@ -71,7 +71,7 @@ describe('ReactTestUtils.act()', () => {
7171
runActTests('legacy sync mode', renderSync, unmountSync, rerenderSync);
7272

7373
// and then in batched mode
74-
let batchedRoot;
74+
let batchedRoot = null;
7575
function renderBatched(el, dom) {
7676
batchedRoot = ReactDOM.unstable_createSyncRoot(dom);
7777
batchedRoot.render(el);
@@ -791,5 +791,26 @@ function runActTests(label, render, unmount, rerender) {
791791
});
792792
}
793793
});
794+
describe('warn in prod mode', () => {
795+
it('warns if you try to use act() in prod mode', () => {
796+
const spy = spyOnDevAndProd(console, 'error');
797+
798+
act(() => {});
799+
800+
if (!__DEV__) {
801+
expect(console.error).toHaveBeenCalledTimes(1);
802+
expect(console.error.calls.argsFor(0)[0]).toContain(
803+
'act(...) is not supported in production builds of React',
804+
);
805+
} else {
806+
expect(console.error).toHaveBeenCalledTimes(0);
807+
}
808+
809+
spy.calls.reset();
810+
// does not warn twice
811+
act(() => {});
812+
expect(console.error).toHaveBeenCalledTimes(0);
813+
});
814+
});
794815
});
795816
}

packages/react-dom/src/test-utils/ReactTestUtilsAct.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,17 @@ function flushWorkAndMicroTasks(onDone: (err: ?Error) => void) {
7474
// so we can tell if any async act() calls try to run in parallel.
7575

7676
let actingUpdatesScopeDepth = 0;
77+
let didWarnAboutUsingActInProd = false;
7778

7879
function act(callback: () => Thenable) {
80+
if (!__DEV__) {
81+
if (didWarnAboutUsingActInProd === false) {
82+
didWarnAboutUsingActInProd = true;
83+
console.error(
84+
'act(...) is not supported in production builds of React, and might not behave as expected.',
85+
);
86+
}
87+
}
7988
let previousActingUpdatesScopeDepth = actingUpdatesScopeDepth;
8089
let previousIsSomeRendererActing;
8190
let previousIsThisRendererActing;

packages/react-noop-renderer/src/createReactNoop.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,8 +630,17 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
630630
// so we can tell if any async act() calls try to run in parallel.
631631

632632
let actingUpdatesScopeDepth = 0;
633+
let didWarnAboutUsingActInProd = false;
633634

634635
function act(callback: () => Thenable) {
636+
if (!__DEV__) {
637+
if (didWarnAboutUsingActInProd === false) {
638+
didWarnAboutUsingActInProd = true;
639+
console.error(
640+
'act(...) is not supported in production builds of React, and might not behave as expected.',
641+
);
642+
}
643+
}
635644
let previousActingUpdatesScopeDepth = actingUpdatesScopeDepth;
636645
let previousIsSomeRendererActing;
637646
let previousIsThisRendererActing;

packages/react-test-renderer/src/ReactTestRendererAct.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,17 @@ function flushWorkAndMicroTasks(onDone: (err: ?Error) => void) {
5555
// so we can tell if any async act() calls try to run in parallel.
5656

5757
let actingUpdatesScopeDepth = 0;
58+
let didWarnAboutUsingActInProd = false;
5859

5960
function act(callback: () => Thenable) {
61+
if (!__DEV__) {
62+
if (didWarnAboutUsingActInProd === false) {
63+
didWarnAboutUsingActInProd = true;
64+
console.error(
65+
'act(...) is not supported in production builds of React, and might not behave as expected.',
66+
);
67+
}
68+
}
6069
let previousActingUpdatesScopeDepth = actingUpdatesScopeDepth;
6170
let previousIsSomeRendererActing;
6271
let previousIsThisRendererActing;

scripts/jest/shouldIgnoreConsoleError.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ module.exports = function shouldIgnoreConsoleError(format, args) {
2525
// They are noisy too so we'll try to ignore them.
2626
return true;
2727
}
28+
if (
29+
format.indexOf(
30+
'act(...) is not supported in production builds of React'
31+
) === 0
32+
) {
33+
// We don't yet support act() for prod builds, and warn for it.
34+
// But we'd like to use act() ourselves for prod builds.
35+
// Let's ignore the warning and #yolo.
36+
return true;
37+
}
2838
}
2939
// Looks legit
3040
return false;

0 commit comments

Comments
 (0)