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.
Sunil Pai committed
Aug 5, 2019 at 21:01 UTC
a1dbb852c29d23d5d0b76a0171eb59df4bebf684
5 files changed
+60
-2
packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js
+23
-2
@@ -27,7 +27,7 @@ function sleep(period) {
27
28
describe('ReactTestUtils.act()', () => {
29
// first we run all the tests with concurrent mode
30
- let concurrentRoot;
30
+ let concurrentRoot = null;
31
function renderConcurrent(el, dom) {
32
concurrentRoot = ReactDOM.unstable_createRoot(dom);
33
concurrentRoot.render(el);
@@ -71,7 +71,7 @@ describe('ReactTestUtils.act()', () => {
71
runActTests('legacy sync mode', renderSync, unmountSync, rerenderSync);
72
73
// and then in batched mode
74
- let batchedRoot;
74
+ let batchedRoot = null;
75
function renderBatched(el, dom) {
76
batchedRoot = ReactDOM.unstable_createSyncRoot(dom);
77
batchedRoot.render(el);
@@ -791,5 +791,26 @@ function runActTests(label, render, unmount, rerender) {
791
});
792
}
793
});
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
+ });
815
});
816
}
packages/react-dom/src/test-utils/ReactTestUtilsAct.js
+9
@@ -74,8 +74,17 @@ function flushWorkAndMicroTasks(onDone: (err: ?Error) => void) {
74
// so we can tell if any async act() calls try to run in parallel.
75
76
let actingUpdatesScopeDepth = 0;
77
+let didWarnAboutUsingActInProd = false;
78
79
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
+ }
88
let previousActingUpdatesScopeDepth = actingUpdatesScopeDepth;
89
let previousIsSomeRendererActing;
90
let previousIsThisRendererActing;
packages/react-noop-renderer/src/createReactNoop.js
+9
@@ -630,8 +630,17 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
630
// so we can tell if any async act() calls try to run in parallel.
631
632
let actingUpdatesScopeDepth = 0;
633
+ let didWarnAboutUsingActInProd = false;
634
635
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
+ }
644
let previousActingUpdatesScopeDepth = actingUpdatesScopeDepth;
645
let previousIsSomeRendererActing;
646
let previousIsThisRendererActing;
packages/react-test-renderer/src/ReactTestRendererAct.js
+9
@@ -55,8 +55,17 @@ function flushWorkAndMicroTasks(onDone: (err: ?Error) => void) {
55
// so we can tell if any async act() calls try to run in parallel.
56
57
let actingUpdatesScopeDepth = 0;
58
+let didWarnAboutUsingActInProd = false;
59
60
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
+ }
69
let previousActingUpdatesScopeDepth = actingUpdatesScopeDepth;
70
let previousIsSomeRendererActing;
71
let previousIsThisRendererActing;
scripts/jest/shouldIgnoreConsoleError.js
+10
@@ -25,6 +25,16 @@ module.exports = function shouldIgnoreConsoleError(format, args) {
25
// They are noisy too so we'll try to ignore them.
26
return true;
27
}
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
+ }
38
}
39
// Looks legit
40
return false;