diff --git a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js index 1a7e3d1f5fe93..43ec803f11555 100644 --- a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js +++ b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js @@ -3370,4 +3370,50 @@ describe('ReactHooksWithNoopRenderer', () => { }); expect(Scheduler).toHaveYielded(['Unmount layout B', 'Unmount passive B']); }); + + it('regression: deleting a tree and unmounting its effects after a reorder', async () => { + const root = ReactNoop.createRoot(); + + function Child({label}) { + useEffect(() => { + Scheduler.unstable_yieldValue('Mount ' + label); + return () => { + Scheduler.unstable_yieldValue('Unmount ' + label); + }; + }, [label]); + return label; + } + + await act(async () => { + root.render( + <> + + + , + ); + }); + expect(Scheduler).toHaveYielded(['Mount A', 'Mount B']); + + await act(async () => { + root.render( + <> + + + , + ); + }); + expect(Scheduler).toHaveYielded([]); + + await act(async () => { + root.render(null); + }); + + expect(Scheduler).toHaveYielded([ + 'Unmount B', + // In the regression, the reorder would cause Child A to "forget" that it + // contains passive effects. Then when we deleted the tree, A's unmount + // effect would not fire. + 'Unmount A', + ]); + }); });