diff --git a/packages/react-reconciler/src/__tests__/ReactOffscreenSuspense-test.js b/packages/react-reconciler/src/__tests__/ReactOffscreenSuspense-test.js
index c73e3caf62..6830bcb426 100644
--- a/packages/react-reconciler/src/__tests__/ReactOffscreenSuspense-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactOffscreenSuspense-test.js
@@ -7,6 +7,7 @@ let Offscreen;
let Suspense;
let useState;
let useEffect;
+let useLayoutEffect;
let startTransition;
let textCache;
let waitFor;
@@ -26,6 +27,7 @@ describe('ReactOffscreen', () => {
Suspense = React.Suspense;
useState = React.useState;
useEffect = React.useEffect;
+ useLayoutEffect = React.useLayoutEffect;
startTransition = React.startTransition;
const InternalTestUtils = require('internal-test-utils');
@@ -551,4 +553,62 @@ describe('ReactOffscreen', () => {
>,
);
});
+
+ it('unmounts/mounts layout effects when re-suspending', async () => {
+ function Child({children}) {
+ useLayoutEffect(() => {
+ Scheduler.log('Mount layout');
+ return () => {
+ Scheduler.log('Unmount layout');
+ };
+ }, []);
+ return {children};
+ }
+
+ const root = ReactNoop.createRoot();
+ await act(() => {
+ root.render(
+ Loading}>
+
+
+
+ ,
+ );
+ });
+ assertLog(['Suspend! [Initial content]']);
+ expect(root).toMatchRenderedOutput(Loading);
+
+ // Resolve suspense, layout effect is mounted.
+ await act(() => {
+ resolveText('Initial content');
+ });
+ assertLog(['Initial content', 'Mount layout']);
+ expect(root).toMatchRenderedOutput(Initial content);
+
+ // Re-suspend, the layout effect is unmounted and content is hidden.
+ await act(() => {
+ root.render(
+ Loading}>
+
+
+
+ ,
+ );
+ });
+ assertLog(['Suspend! [Re-suspend]', 'Unmount layout']);
+ expect(root).toMatchRenderedOutput(
+ <>
+ Initial content
+ Loading
+ >,
+ );
+
+ // Resolve suspense again, layout effect is re-mounted.
+ await act(() => {
+ resolveText('Re-suspend');
+ });
+
+ assertLog(['Re-suspend', 'Mount layout']);
+ expect(root).toMatchRenderedOutput(Re-suspend);
+ });
});