@@ -310,6 +310,47 @@ describe('ReactOffscreen', () => {
310310 expect ( root ) . toMatchRenderedOutput ( < span hidden = { true } prop = "Child" /> ) ;
311311 } ) ;
312312
313+ // @gate experimental || www
314+ // @gate enableSuspenseLayoutEffectSemantics
315+ // @gate enableFlipOffscreenUnhideOrder
316+ it ( 'hides children of offscreen after layout effects are destroyed' , async ( ) => {
317+ const root = ReactNoop . createRoot ( ) ;
318+ function Child ( { text} ) {
319+ useLayoutEffect ( ( ) => {
320+ Scheduler . unstable_yieldValue ( 'Mount layout' ) ;
321+ return ( ) => {
322+ // The child should not be hidden yet.
323+ expect ( root ) . toMatchRenderedOutput ( < span prop = "Child" /> ) ;
324+ Scheduler . unstable_yieldValue ( 'Unmount layout' ) ;
325+ } ;
326+ } , [ ] ) ;
327+ return < Text text = "Child" /> ;
328+ }
329+
330+ await act ( async ( ) => {
331+ root . render (
332+ < Offscreen mode = "visible" >
333+ < Child />
334+ </ Offscreen > ,
335+ ) ;
336+ } ) ;
337+ expect ( Scheduler ) . toHaveYielded ( [ 'Child' , 'Mount layout' ] ) ;
338+ expect ( root ) . toMatchRenderedOutput ( < span prop = "Child" /> ) ;
339+
340+ // Hide the tree. The layout effect is unmounted.
341+ await act ( async ( ) => {
342+ root . render (
343+ < Offscreen mode = "hidden" >
344+ < Child />
345+ </ Offscreen > ,
346+ ) ;
347+ } ) ;
348+ expect ( Scheduler ) . toHaveYielded ( [ 'Unmount layout' , 'Child' ] ) ;
349+
350+ // After the layout effect is unmounted, the child is hidden.
351+ expect ( root ) . toMatchRenderedOutput ( < span hidden = { true } prop = "Child" /> ) ;
352+ } ) ;
353+
313354 // @gate www
314355 it ( 'does not toggle effects for LegacyHidden component' , async ( ) => {
315356 // LegacyHidden is meant to be the same as offscreen except it doesn't
0 commit comments