diff --git a/packages/react-dom/src/__tests__/ReactDOMHooks-test.js b/packages/react-dom/src/__tests__/ReactDOMHooks-test.js index 896c8c0acac01..aff3003fc0ceb 100644 --- a/packages/react-dom/src/__tests__/ReactDOMHooks-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMHooks-test.js @@ -53,32 +53,23 @@ describe('ReactDOMHooks', () => { return 3 * n; } - // we explicitly catch the missing act() warnings - // to simulate this tricky repro - expect(() => { - ReactDOM.render(, 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(, 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(, 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(, 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', () => { diff --git a/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js b/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js index fb1027a306289..ac37e5ba07ac9 100644 --- a/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js +++ b/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js @@ -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) { @@ -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(, 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(() => { diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index 1c5d14651fc27..f8a389e63577b 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -2453,6 +2453,7 @@ export function warnIfNotCurrentlyActingEffectsInDEV(fiber: Fiber): void { if (__DEV__) { if ( warnsIfNotActing === true && + fiber.mode && IsSomeRendererActing.current === false && IsThisRendererActing.current === false ) { diff --git a/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js b/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js index e0022d0a70c22..eff2904158a72 100644 --- a/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js @@ -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.