diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index 49a4c53c8941..d2081c8359d3 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -88,6 +88,7 @@ import { NoFlags, PerformedWork, Placement, + PlacementDEV, Hydrating, Callback, ContentReset, @@ -3859,7 +3860,7 @@ function remountFiber( deletions.push(current); } - newWorkInProgress.flags |= Placement; + newWorkInProgress.flags |= Placement | PlacementDEV; // Restart work from the new fiber. return newWorkInProgress; diff --git a/packages/react-refresh/src/__tests__/ReactFresh-test.js b/packages/react-refresh/src/__tests__/ReactFresh-test.js index 6fb00a66a24b..e0f2d490335e 100644 --- a/packages/react-refresh/src/__tests__/ReactFresh-test.js +++ b/packages/react-refresh/src/__tests__/ReactFresh-test.js @@ -2326,6 +2326,98 @@ describe('ReactFresh', () => { expect(finalEl.style.color).toBe('orange'); } + it('double invokes effects after a forced remount in StrictMode', async () => { + if (__DEV__) { + const log = []; + + const createAppV1 = () => { + function Hello() { + React.useEffect(() => { + log.push('mount v1'); + return () => log.push('unmount v1'); + }, []); + return

Hello

; + } + $RefreshReg$(Hello, 'Hello'); + $RefreshSig$(Hello, '1'); + + return Hello; + }; + + const App = createAppV1(); + + await act(() => { + root.render( + + + , + ); + }); + + expect(log).toEqual(['mount v1', 'unmount v1', 'mount v1']); + log.length = 0; + + await patch(() => { + function Hello() { + React.useEffect(() => { + log.push('mount v2'); + return () => log.push('unmount v2'); + }, []); + return

Hello

; + } + $RefreshReg$(Hello, 'Hello'); + $RefreshSig$(Hello, '2'); + return null; + }); + + expect(container.firstChild.style.color).toBe('red'); + expect(log).toEqual(['unmount v1', 'mount v2', 'unmount v2', 'mount v2']); + } + }); + + it('double invokes an effect added during Fast Refresh remount in StrictMode', async () => { + if (__DEV__) { + const log = []; + + const createAppV1 = () => { + function Hello() { + return

Hello

; + } + $RefreshReg$(Hello, 'Hello'); + $RefreshSig$(Hello, '1'); + return Hello; + }; + + const App = createAppV1(); + + await act(() => { + root.render( + + + , + ); + }); + + expect(log).toEqual([]); + + await patch(() => { + function Hello() { + React.useEffect(() => { + log.push('mount v2'); + return () => log.push('unmount v2'); + }, []); + return

Hello

; + } + $RefreshReg$(Hello, 'Hello'); + $RefreshSig$(Hello, '2'); + return null; + }); + + expect(container.firstChild.style.color).toBe('red'); + expect(log).toEqual(['mount v2', 'unmount v2', 'mount v2']); + } + }); + it('resets hooks with dependencies on hot reload', async () => { if (__DEV__) { let useEffectWithEmptyArrayCalls = 0;