Skip to content

Commit 2f22a8b

Browse files
committed
Warn if act is called in wrong environment
Adds a warning if `act` is called but `IS_REACT_ACT_ENVIRONMENT` is not enabled. The goal is to prompt users to correctly configure their testing environment, so that if they forget to use `act` in a different test, we can detect and warn about. It's expected that the environment flag will be configured by the testing framework. For example, a Jest plugin. We will link to the relevant documentation page, once it exists. The warning only fires in concurrent mode. Legacy roots will keep the existing behavior.
1 parent 29a0fd3 commit 2f22a8b

File tree

7 files changed

+92
-0
lines changed

7 files changed

+92
-0
lines changed

packages/react-devtools-scheduling-profiler/src/import-worker/__tests__/preprocessData-test.internal.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import {
1717
} from '../../constants';
1818
import REACT_VERSION from 'shared/ReactVersion';
1919

20+
global.IS_REACT_ACT_ENVIRONMENT = true;
21+
2022
describe('getLanesFromTransportDecimalBitmask', () => {
2123
it('should return array of lane numbers from bitmask string', () => {
2224
expect(getLanesFromTransportDecimalBitmask('1')).toEqual([0]);

packages/react-devtools-shared/src/__tests__/inspectedElement-test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ describe('InspectedElement', () => {
3838
let ErrorBoundary;
3939
let errorBoundaryInstance;
4040

41+
global.IS_REACT_ACT_ENVIRONMENT = true;
42+
4143
beforeEach(() => {
4244
utils = require('./utils');
4345
utils.beforeEachProfiling();

packages/react-reconciler/src/ReactFiberAct.new.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,14 @@
88
*/
99

1010
import type {Fiber} from './ReactFiber.new';
11+
12+
import ReactSharedInternals from 'shared/ReactSharedInternals';
13+
1114
import {warnsIfNotActing} from './ReactFiberHostConfig';
1215
import {ConcurrentMode} from './ReactTypeOfMode';
1316

17+
const {ReactCurrentActQueue} = ReactSharedInternals;
18+
1419
export function isActEnvironment(fiber: Fiber) {
1520
if (__DEV__) {
1621
const isReactActEnvironmentGlobal =
@@ -20,6 +25,16 @@ export function isActEnvironment(fiber: Fiber) {
2025
: undefined;
2126

2227
if (fiber.mode & ConcurrentMode) {
28+
if (
29+
!isReactActEnvironmentGlobal &&
30+
ReactCurrentActQueue.current !== null
31+
) {
32+
// TODO: Include link to relevant documentation page.
33+
console.error(
34+
'The current testing environment is not configured to support ' +
35+
'act(...)',
36+
);
37+
}
2338
return isReactActEnvironmentGlobal;
2439
} else {
2540
// Legacy mode. We preserve the behavior of React 17's act. It assumes an

packages/react-reconciler/src/ReactFiberAct.old.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,14 @@
88
*/
99

1010
import type {Fiber} from './ReactFiber.old';
11+
12+
import ReactSharedInternals from 'shared/ReactSharedInternals';
13+
1114
import {warnsIfNotActing} from './ReactFiberHostConfig';
1215
import {ConcurrentMode} from './ReactTypeOfMode';
1316

17+
const {ReactCurrentActQueue} = ReactSharedInternals;
18+
1419
export function isActEnvironment(fiber: Fiber) {
1520
if (__DEV__) {
1621
const isReactActEnvironmentGlobal =
@@ -20,6 +25,16 @@ export function isActEnvironment(fiber: Fiber) {
2025
: undefined;
2126

2227
if (fiber.mode & ConcurrentMode) {
28+
if (
29+
!isReactActEnvironmentGlobal &&
30+
ReactCurrentActQueue.current !== null
31+
) {
32+
// TODO: Include link to relevant documentation page.
33+
console.error(
34+
'The current testing environment is not configured to support ' +
35+
'act(...)',
36+
);
37+
}
2338
return isReactActEnvironmentGlobal;
2439
} else {
2540
// Legacy mode. We preserve the behavior of React 17's act. It assumes an

packages/react-reconciler/src/__tests__/DebugTracing-test.internal.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ describe('DebugTracing', () => {
1919
const DEFAULT_LANE_STRING = '0b0000000000000000000000000010000';
2020
const RETRY_LANE_STRING = '0b0000000010000000000000000000000';
2121

22+
global.IS_REACT_ACT_ENVIRONMENT = true;
23+
2224
beforeEach(() => {
2325
jest.resetModules();
2426

packages/react-reconciler/src/__tests__/ReactActWarnings-test.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,18 @@ let React;
1111
let Scheduler;
1212
let ReactNoop;
1313
let useState;
14+
let act;
1415

1516
// These tests are mostly concerned with concurrent roots. The legacy root
1617
// behavior is covered by other older test suites and is unchanged from
1718
// React 17.
1819
describe('act warnings', () => {
1920
beforeEach(() => {
21+
jest.resetModules();
2022
React = require('react');
2123
Scheduler = require('scheduler');
2224
ReactNoop = require('react-noop-renderer');
25+
act = React.unstable_act;
2326
useState = React.useState;
2427
});
2528

@@ -73,4 +76,55 @@ describe('act warnings', () => {
7376
expect(root).toMatchRenderedOutput('3');
7477
});
7578
});
79+
80+
// @gate __DEV__
81+
test('act warns if the environment flag is not enabled', () => {
82+
let setState;
83+
function App() {
84+
const [state, _setState] = useState(0);
85+
setState = _setState;
86+
return <Text text={state} />;
87+
}
88+
89+
const root = ReactNoop.createRoot();
90+
root.render(<App />);
91+
expect(Scheduler).toFlushAndYield([0]);
92+
expect(root).toMatchRenderedOutput('0');
93+
94+
// Default behavior. Flag is undefined. Warn.
95+
expect(global.IS_REACT_ACT_ENVIRONMENT).toBe(undefined);
96+
expect(() => {
97+
act(() => {
98+
setState(1);
99+
});
100+
}).toErrorDev(
101+
'The current testing environment is not configured to support act(...)',
102+
{withoutStack: true},
103+
);
104+
expect(Scheduler).toHaveYielded([1]);
105+
expect(root).toMatchRenderedOutput('1');
106+
107+
// Flag is true. Don't warn.
108+
withActEnvironment(true, () => {
109+
act(() => {
110+
setState(2);
111+
});
112+
expect(Scheduler).toHaveYielded([2]);
113+
expect(root).toMatchRenderedOutput('2');
114+
});
115+
116+
// Flag is false. Warn.
117+
withActEnvironment(false, () => {
118+
expect(() => {
119+
act(() => {
120+
setState(1);
121+
});
122+
}).toErrorDev(
123+
'The current testing environment is not configured to support act(...)',
124+
{withoutStack: true},
125+
);
126+
expect(Scheduler).toHaveYielded([1]);
127+
expect(root).toMatchRenderedOutput('1');
128+
});
129+
});
76130
});

packages/react-reconciler/src/__tests__/SchedulingProfilerLabels-test.internal.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ describe('SchedulingProfiler labels', () => {
2121
let featureDetectionMarkName = null;
2222
let marks;
2323

24+
global.IS_REACT_ACT_ENVIRONMENT = true;
25+
2426
function polyfillJSDomUserTiming() {
2527
featureDetectionMarkName = null;
2628

0 commit comments

Comments
 (0)