diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index 53ac747aa4..d634fc5c70 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -1684,7 +1684,8 @@ function resetChildLanes(completedWork: Fiber) { (completedWork.tag === LegacyHiddenComponent || completedWork.tag === OffscreenComponent) && completedWork.memoizedState !== null && - !includesSomeLane(subtreeRenderLanes, (OffscreenLane: Lane)) + !includesSomeLane(subtreeRenderLanes, (OffscreenLane: Lane)) && + (completedWork.mode & ConcurrentMode) !== NoLanes ) { // The children of this component are hidden. Don't bubble their // expiration times. diff --git a/packages/react-reconciler/src/__tests__/ReactOffscreen-test.js b/packages/react-reconciler/src/__tests__/ReactOffscreen-test.js index c85f333067..9e3cb7bedc 100644 --- a/packages/react-reconciler/src/__tests__/ReactOffscreen-test.js +++ b/packages/react-reconciler/src/__tests__/ReactOffscreen-test.js @@ -2,6 +2,7 @@ let React; let ReactNoop; let Scheduler; let LegacyHidden; +let useState; describe('ReactOffscreen', () => { beforeEach(() => { @@ -11,6 +12,7 @@ describe('ReactOffscreen', () => { ReactNoop = require('react-noop-renderer'); Scheduler = require('scheduler'); LegacyHidden = React.unstable_LegacyHidden; + useState = React.useState; }); function Text(props) { @@ -77,4 +79,90 @@ describe('ReactOffscreen', () => { , ); }); + + // @gate experimental + // @gate new + it('does not defer in legacy mode', async () => { + let setState; + function Foo() { + const [state, _setState] = useState('A'); + setState = _setState; + return ; + } + + const root = ReactNoop.createLegacyRoot(); + await ReactNoop.act(async () => { + root.render( + <> + + + + + , + ); + // Should not defer the hidden tree + expect(Scheduler).toFlushUntilNextPaint(['A', 'Outside']); + }); + expect(root).toMatchRenderedOutput( + <> + + + , + ); + + // Test that the children can be updated + await ReactNoop.act(async () => { + setState('B'); + }); + expect(Scheduler).toHaveYielded(['B']); + expect(root).toMatchRenderedOutput( + <> + + + , + ); + }); + + // @gate experimental + // @gate new + it('does not defer in blocking mode', async () => { + let setState; + function Foo() { + const [state, _setState] = useState('A'); + setState = _setState; + return ; + } + + const root = ReactNoop.createBlockingRoot(); + await ReactNoop.act(async () => { + root.render( + <> + + + + + , + ); + // Should not defer the hidden tree + expect(Scheduler).toFlushUntilNextPaint(['A', 'Outside']); + }); + expect(root).toMatchRenderedOutput( + <> + + + , + ); + + // Test that the children can be updated + await ReactNoop.act(async () => { + setState('B'); + }); + expect(Scheduler).toHaveYielded(['B']); + expect(root).toMatchRenderedOutput( + <> + + + , + ); + }); });