Skip to content

Commit

Permalink
only warn on unacted effects for strict / non sync modes
Browse files Browse the repository at this point in the history
(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.
  • Loading branch information
threepointone committed Jul 2, 2019
1 parent a457e02 commit 4b466ce
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 27 deletions.
43 changes: 17 additions & 26 deletions packages/react-dom/src/__tests__/ReactDOMHooks-test.js
Expand Up @@ -53,32 +53,23 @@ describe('ReactDOMHooks', () => {
return 3 * n;
}

// we explicitly catch the missing act() warnings
// to simulate this tricky repro
expect(() => {
ReactDOM.render(<Example1 n={1} />, container);
expect(container.textContent).toBe('1');
expect(container2.textContent).toBe('');
expect(container3.textContent).toBe('');
Scheduler.unstable_flushAll();
expect(container.textContent).toBe('1');
expect(container2.textContent).toBe('2');
expect(container3.textContent).toBe('3');

ReactDOM.render(<Example1 n={2} />, container);
expect(container.textContent).toBe('2');
expect(container2.textContent).toBe('2'); // Not flushed yet
expect(container3.textContent).toBe('3'); // Not flushed yet
Scheduler.unstable_flushAll();
expect(container.textContent).toBe('2');
expect(container2.textContent).toBe('4');
expect(container3.textContent).toBe('6');
}).toWarnDev([
'An update to Example1 ran an effect',
'An update to Example2 ran an effect',
'An update to Example1 ran an effect',
'An update to Example2 ran an effect',
]);
ReactDOM.render(<Example1 n={1} />, container);
expect(container.textContent).toBe('1');
expect(container2.textContent).toBe('');
expect(container3.textContent).toBe('');
Scheduler.unstable_flushAll();
expect(container.textContent).toBe('1');
expect(container2.textContent).toBe('2');
expect(container3.textContent).toBe('3');

ReactDOM.render(<Example1 n={2} />, container);
expect(container.textContent).toBe('2');
expect(container2.textContent).toBe('2'); // Not flushed yet
expect(container3.textContent).toBe('3'); // Not flushed yet
Scheduler.unstable_flushAll();
expect(container.textContent).toBe('2');
expect(container2.textContent).toBe('4');
expect(container3.textContent).toBe('6');
});

it('should not bail out when an update is scheduled from within an event handler', () => {
Expand Down
34 changes: 34 additions & 0 deletions packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js
Expand Up @@ -48,6 +48,20 @@ describe('ReactTestUtils.act()', () => {
ReactDOM.unmountComponentAtNode(dom);
}
runActTests('legacy sync mode', renderSync, unmountSync);

// and then in batched mode
let batchedRoot;
function renderBatched(el, dom) {
batchedRoot = ReactDOM.unstable_createSyncRoot(dom);
batchedRoot.render(el);
}
function unmountBatched(dom) {
if (batchedRoot !== null) {
batchedRoot.unmount();
batchedRoot = null;
}
}
runActTests('batched mode', renderBatched, unmountBatched);
});

function runActTests(label, render, unmount) {
Expand All @@ -68,6 +82,26 @@ function runActTests(label, render, unmount) {
document.body.removeChild(container);
});
describe('sync', () => {
it('warns if an effect is queued outside an act scope, except in legacy sync+non-strict mode', () => {
function App() {
React.useEffect(() => {}, []);
return null;
}
expect(() => {
render(<App />, container);
// flush all queued work
Scheduler.unstable_flushAll();
}).toWarnDev(
label !== 'legacy sync mode'
? [
// warns twice because we're in strict+dev mode
'An update to App ran an effect, but was not wrapped in act(...)',
'An update to App ran an effect, but was not wrapped in act(...)',
]
: [],
);
});

it('can use act to flush effects', () => {
function App() {
React.useEffect(() => {
Expand Down
1 change: 1 addition & 0 deletions packages/react-reconciler/src/ReactFiberWorkLoop.js
Expand Up @@ -2453,6 +2453,7 @@ export function warnIfNotCurrentlyActingEffectsInDEV(fiber: Fiber): void {
if (__DEV__) {
if (
warnsIfNotActing === true &&
fiber.mode &&
IsSomeRendererActing.current === false &&
IsThisRendererActing.current === false
) {
Expand Down
Expand Up @@ -1806,7 +1806,6 @@ describe('ReactHooks', () => {
globalListener();
globalListener();
}).toWarnDev([
'An update to C ran an effect',
'An update to C inside a test was not wrapped in act',
'An update to C inside a test was not wrapped in act',
// Note: should *not* warn about updates on unmounted component.
Expand Down

0 comments on commit 4b466ce

Please sign in to comment.