diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.js b/packages/react-reconciler/src/ReactFiberCommitWork.js index 6d600971d4461..71a2cce6c0ce8 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.js @@ -1324,58 +1324,56 @@ function commitDeletionEffectsOnFiber( case ForwardRef: case MemoComponent: case SimpleMemoComponent: { - if (!offscreenSubtreeWasHidden) { - const updateQueue: FunctionComponentUpdateQueue | null = - (deletedFiber.updateQueue: any); - if (updateQueue !== null) { - const lastEffect = updateQueue.lastEffect; - if (lastEffect !== null) { - const firstEffect = lastEffect.next; - - let effect = firstEffect; - do { - const tag = effect.tag; - const inst = effect.inst; - const destroy = inst.destroy; - if (destroy !== undefined) { - if ((tag & HookInsertion) !== NoHookEffect) { + const updateQueue: FunctionComponentUpdateQueue | null = + (deletedFiber.updateQueue: any); + if (updateQueue !== null) { + const lastEffect = updateQueue.lastEffect; + if (lastEffect !== null) { + const firstEffect = lastEffect.next; + + let effect = firstEffect; + do { + const tag = effect.tag; + const inst = effect.inst; + const destroy = inst.destroy; + if (destroy !== undefined) { + if ((tag & HookInsertion) !== NoHookEffect) { + inst.destroy = undefined; + safelyCallDestroy( + deletedFiber, + nearestMountedAncestor, + destroy, + ); + } else if ((tag & HookLayout) !== NoHookEffect) { + if (enableSchedulingProfiler) { + markComponentLayoutEffectUnmountStarted(deletedFiber); + } + + if (shouldProfile(deletedFiber)) { + startLayoutEffectTimer(); inst.destroy = undefined; safelyCallDestroy( deletedFiber, nearestMountedAncestor, destroy, ); - } else if ((tag & HookLayout) !== NoHookEffect) { - if (enableSchedulingProfiler) { - markComponentLayoutEffectUnmountStarted(deletedFiber); - } - - if (shouldProfile(deletedFiber)) { - startLayoutEffectTimer(); - inst.destroy = undefined; - safelyCallDestroy( - deletedFiber, - nearestMountedAncestor, - destroy, - ); - recordLayoutEffectDuration(deletedFiber); - } else { - inst.destroy = undefined; - safelyCallDestroy( - deletedFiber, - nearestMountedAncestor, - destroy, - ); - } + recordLayoutEffectDuration(deletedFiber); + } else { + inst.destroy = undefined; + safelyCallDestroy( + deletedFiber, + nearestMountedAncestor, + destroy, + ); + } - if (enableSchedulingProfiler) { - markComponentLayoutEffectUnmountStopped(); - } + if (enableSchedulingProfiler) { + markComponentLayoutEffectUnmountStopped(); } } - effect = effect.next; - } while (effect !== firstEffect); - } + } + effect = effect.next; + } while (effect !== firstEffect); } } diff --git a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js index 0fc64b39da899..e70c6588d876e 100644 --- a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js +++ b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js @@ -2872,6 +2872,34 @@ describe('ReactHooksWithNoopRenderer', () => { }); }); + // @gate enableActivity + it('runs insertion effect cleanup when unmounting in Offscreen state', async () => { + function Logger(props) { + useInsertionEffect(() => { + Scheduler.log(`create`); + return () => { + Scheduler.log(`destroy`); + }; + }, []); + return null; + } + + const Activity = React.unstable_Activity; + await act(async () => { + ReactNoop.render( + + + , + ); + await waitForAll(['create']); + }); + + await act(async () => { + ReactNoop.render(null); + await waitForAll(['destroy']); + }); + }); + it('assumes insertion effect destroy function is either a function or undefined', async () => { function App(props) { useInsertionEffect(() => {