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();
- return (
-
-
+ await act(async () => {
+ // Inner offscreen is visible.
+ root.render(
+
+
-
+ ,
);
- }
+ });
+
+ expect(Scheduler).toHaveYielded(['child']);
+ expect(root).toMatchRenderedOutput();
- const root = ReactNoop.createRoot();
await act(async () => {
- root.render();
+ // Inner offscreen is hidden.
+ root.render(
+
+
+
+
+ ,
+ );
});
- expect(Scheduler).toHaveYielded(['Child']);
- expect(root).toMatchRenderedOutput();
+ expect(Scheduler).toHaveYielded(['child']);
+ expect(root).toMatchRenderedOutput();
await act(async () => {
- _setIsVisible(true);
+ // Inner offscreen is visible.
+ root.render(
+
+
+
+
+ ,
+ );
});
- expect(Scheduler).toHaveYielded(['Child']);
- expect(root).toMatchRenderedOutput();
+ Scheduler.unstable_clearYields();
+
+ await act(async () => {
+ // Outer offscreen is visible.
+ // Inner offscreen is hidden.
+ root.render(
+
+
+
+
+ ,
+ );
+ });
+
+ expect(Scheduler).toHaveYielded(['child']);
await act(async () => {
- _setIsVisible(false);
+ // Outer offscreen is hidden.
+ // Inner offscreen is visible.
+ root.render(
+
+
+
+
+ ,
+ );
});
- expect(Scheduler).toHaveYielded(['Child']);
- expect(root).toMatchRenderedOutput();
+ expect(Scheduler).toHaveYielded(['child']);
});
// @gate enableOffscreen