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(
+ <>
+
+
+ >,
+ );
+ });
});