Skip to content

Commit a98cd03

Browse files
Sunil Paitrueadm
Sunil Pai
authored andcommitted
[fail] Only warn on unacted effects for strict / non sync modes (facebook#16041)
* only warn on unacted effects for strict / non sync modes (basically, when `fiber.mode !== 0b0000`) Warnings on unacted effects may be too noisy, especially for legacy apps. This PR fires the warning only when in a non sync mode (concurrent/batched), or when in strict mode. This should make updating easier. I also added batched mode tests to the act() suite. * explicitly check for modes before warning, explicit tests for all modes
1 parent e9aa2e5 commit a98cd03

File tree

4 files changed

+89
-27
lines changed

4 files changed

+89
-27
lines changed

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

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -53,32 +53,23 @@ describe('ReactDOMHooks', () => {
5353
return 3 * n;
5454
}
5555

56-
// we explicitly catch the missing act() warnings
57-
// to simulate this tricky repro
58-
expect(() => {
59-
ReactDOM.render(<Example1 n={1} />, container);
60-
expect(container.textContent).toBe('1');
61-
expect(container2.textContent).toBe('');
62-
expect(container3.textContent).toBe('');
63-
Scheduler.unstable_flushAll();
64-
expect(container.textContent).toBe('1');
65-
expect(container2.textContent).toBe('2');
66-
expect(container3.textContent).toBe('3');
67-
68-
ReactDOM.render(<Example1 n={2} />, container);
69-
expect(container.textContent).toBe('2');
70-
expect(container2.textContent).toBe('2'); // Not flushed yet
71-
expect(container3.textContent).toBe('3'); // Not flushed yet
72-
Scheduler.unstable_flushAll();
73-
expect(container.textContent).toBe('2');
74-
expect(container2.textContent).toBe('4');
75-
expect(container3.textContent).toBe('6');
76-
}).toWarnDev([
77-
'An update to Example1 ran an effect',
78-
'An update to Example2 ran an effect',
79-
'An update to Example1 ran an effect',
80-
'An update to Example2 ran an effect',
81-
]);
56+
ReactDOM.render(<Example1 n={1} />, container);
57+
expect(container.textContent).toBe('1');
58+
expect(container2.textContent).toBe('');
59+
expect(container3.textContent).toBe('');
60+
Scheduler.unstable_flushAll();
61+
expect(container.textContent).toBe('1');
62+
expect(container2.textContent).toBe('2');
63+
expect(container3.textContent).toBe('3');
64+
65+
ReactDOM.render(<Example1 n={2} />, container);
66+
expect(container.textContent).toBe('2');
67+
expect(container2.textContent).toBe('2'); // Not flushed yet
68+
expect(container3.textContent).toBe('3'); // Not flushed yet
69+
Scheduler.unstable_flushAll();
70+
expect(container.textContent).toBe('2');
71+
expect(container2.textContent).toBe('4');
72+
expect(container3.textContent).toBe('6');
8273
});
8374

8475
it('should not bail out when an update is scheduled from within an event handler', () => {

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

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,73 @@ describe('ReactTestUtils.act()', () => {
4848
ReactDOM.unmountComponentAtNode(dom);
4949
}
5050
runActTests('legacy sync mode', renderSync, unmountSync);
51+
52+
// and then in batched mode
53+
let batchedRoot;
54+
function renderBatched(el, dom) {
55+
batchedRoot = ReactDOM.unstable_createSyncRoot(dom);
56+
batchedRoot.render(el);
57+
}
58+
function unmountBatched(dom) {
59+
if (batchedRoot !== null) {
60+
batchedRoot.unmount();
61+
batchedRoot = null;
62+
}
63+
}
64+
runActTests('batched mode', renderBatched, unmountBatched);
65+
66+
describe('unacted effects', () => {
67+
function App() {
68+
React.useEffect(() => {}, []);
69+
return null;
70+
}
71+
72+
it('does not warn in legacy sync mode', () => {
73+
expect(() => {
74+
ReactDOM.render(<App />, document.createElement('div'));
75+
}).toWarnDev([]);
76+
});
77+
78+
it('warns in strict mode', () => {
79+
expect(() => {
80+
ReactDOM.render(
81+
<React.StrictMode>
82+
<App />
83+
</React.StrictMode>,
84+
document.createElement('div'),
85+
);
86+
}).toWarnDev([
87+
'An update to App ran an effect, but was not wrapped in act(...)',
88+
'An update to App ran an effect, but was not wrapped in act(...)',
89+
]);
90+
});
91+
92+
it('warns in batched mode', () => {
93+
expect(() => {
94+
const root = ReactDOM.unstable_createSyncRoot(
95+
document.createElement('div'),
96+
);
97+
root.render(<App />);
98+
Scheduler.unstable_flushAll();
99+
}).toWarnDev([
100+
'An update to App ran an effect, but was not wrapped in act(...)',
101+
'An update to App ran an effect, but was not wrapped in act(...)',
102+
]);
103+
});
104+
105+
it('warns in concurrent mode', () => {
106+
expect(() => {
107+
const root = ReactDOM.unstable_createRoot(
108+
document.createElement('div'),
109+
);
110+
root.render(<App />);
111+
Scheduler.unstable_flushAll();
112+
}).toWarnDev([
113+
'An update to App ran an effect, but was not wrapped in act(...)',
114+
'An update to App ran an effect, but was not wrapped in act(...)',
115+
]);
116+
});
117+
});
51118
});
52119

53120
function runActTests(label, render, unmount) {

packages/react-reconciler/src/ReactFiberWorkLoop.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import {
6161
import {createWorkInProgress, assignFiberPropertiesInDEV} from './ReactFiber';
6262
import {
6363
NoMode,
64+
StrictMode,
6465
ProfileMode,
6566
BatchedMode,
6667
ConcurrentMode,
@@ -2453,6 +2454,10 @@ export function warnIfNotCurrentlyActingEffectsInDEV(fiber: Fiber): void {
24532454
if (__DEV__) {
24542455
if (
24552456
warnsIfNotActing === true &&
2457+
(fiber.mode & StrictMode ||
2458+
fiber.mode & ProfileMode ||
2459+
fiber.mode & BatchedMode ||
2460+
fiber.mode & ConcurrentMode) &&
24562461
IsSomeRendererActing.current === false &&
24572462
IsThisRendererActing.current === false
24582463
) {

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1806,7 +1806,6 @@ describe('ReactHooks', () => {
18061806
globalListener();
18071807
globalListener();
18081808
}).toWarnDev([
1809-
'An update to C ran an effect',
18101809
'An update to C inside a test was not wrapped in act',
18111810
'An update to C inside a test was not wrapped in act',
18121811
// Note: should *not* warn about updates on unmounted component.

0 commit comments

Comments
 (0)