diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.new.js b/packages/react-reconciler/src/ReactFiberCommitWork.new.js index 0b885bf13488b..1a9b72c8aeb0b 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.new.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.new.js @@ -2880,12 +2880,13 @@ function commitMutationEffectsOnFiber( if (isHidden) { const isUpdate = current !== null; - const isAncestorOffscreenHidden = offscreenSubtreeIsHidden; + const wasHiddenByAncestorOffscreen = + offscreenSubtreeIsHidden || offscreenSubtreeWasHidden; // Only trigger disapper layout effects if: // - This is an update, not first mount. // - This Offscreen was not hidden before. - // - No ancestor Offscreen is hidden. - if (isUpdate && !wasHidden && !isAncestorOffscreenHidden) { + // - Ancestor Offscreen was not hidden in previous commit. + if (isUpdate && !wasHidden && !wasHiddenByAncestorOffscreen) { if ((finishedWork.mode & ConcurrentMode) !== NoMode) { // Disappear the layout effects of all the children recursivelyTraverseDisappearLayoutEffects(finishedWork); diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.old.js b/packages/react-reconciler/src/ReactFiberCommitWork.old.js index cbbca982127f0..a873d23c3d729 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.old.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.old.js @@ -2880,12 +2880,13 @@ function commitMutationEffectsOnFiber( if (isHidden) { const isUpdate = current !== null; - const isAncestorOffscreenHidden = offscreenSubtreeIsHidden; + const wasHiddenByAncestorOffscreen = + offscreenSubtreeIsHidden || offscreenSubtreeWasHidden; // Only trigger disapper layout effects if: // - This is an update, not first mount. // - This Offscreen was not hidden before. - // - No ancestor Offscreen is hidden. - if (isUpdate && !wasHidden && !isAncestorOffscreenHidden) { + // - Ancestor Offscreen was not hidden in previous commit. + if (isUpdate && !wasHidden && !wasHiddenByAncestorOffscreen) { if ((finishedWork.mode & ConcurrentMode) !== NoMode) { // Disappear the layout effects of all the children recursivelyTraverseDisappearLayoutEffects(finishedWork); diff --git a/packages/react-reconciler/src/__tests__/ReactOffscreen-test.js b/packages/react-reconciler/src/__tests__/ReactOffscreen-test.js index 417f851ad45b7..d830265610823 100644 --- a/packages/react-reconciler/src/__tests__/ReactOffscreen-test.js +++ b/packages/react-reconciler/src/__tests__/ReactOffscreen-test.js @@ -275,7 +275,7 @@ describe('ReactOffscreen', () => { // goes from visible to hidden in synchronous update. class ClassComponent extends React.Component { render() { - return ; + return ; } componentWillUnmount() { @@ -287,47 +287,89 @@ describe('ReactOffscreen', () => { } } - let _setIsVisible; - let isFirstRender = true; + const root = ReactNoop.createRoot(); + await act(async () => { + // Outer and inner offscreen are hidden. + root.render( + + + + + , + ); + }); - function App() { - const [isVisible, setIsVisible] = React.useState(true); - _setIsVisible = setIsVisible; - if (isFirstRender === true) { - setIsVisible(false); - isFirstRender = false; - } + expect(Scheduler).toHaveYielded(['child']); + expect(root).toMatchRenderedOutput(