Skip to content

Commit c3b7412

Browse files
committed
Add detach to Offscreen component
1 parent d8611ac commit c3b7412

12 files changed

+288
-14
lines changed

packages/react-reconciler/src/ReactFiber.new.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,7 @@ export function createFiberFromOffscreen(
723723
_pendingMarkers: null,
724724
_retryCache: null,
725725
_transitions: null,
726+
detach: () => {},
726727
};
727728
fiber.stateNode = primaryChildInstance;
728729
return fiber;
@@ -744,6 +745,7 @@ export function createFiberFromLegacyHidden(
744745
_pendingMarkers: null,
745746
_transitions: null,
746747
_retryCache: null,
748+
detach: () => {},
747749
};
748750
fiber.stateNode = instance;
749751
return fiber;

packages/react-reconciler/src/ReactFiber.old.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,7 @@ export function createFiberFromOffscreen(
723723
_pendingMarkers: null,
724724
_retryCache: null,
725725
_transitions: null,
726+
detach: () => {},
726727
};
727728
fiber.stateNode = primaryChildInstance;
728729
return fiber;
@@ -744,6 +745,7 @@ export function createFiberFromLegacyHidden(
744745
_pendingMarkers: null,
745746
_transitions: null,
746747
_retryCache: null,
748+
detach: () => {},
747749
};
748750
fiber.stateNode = instance;
749751
return fiber;

packages/react-reconciler/src/ReactFiberBeginWork.new.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import {
4141
enableCPUSuspense,
4242
enableUseMutableSource,
4343
} from 'shared/ReactFeatureFlags';
44-
44+
import {OffscreenDetached} from './ReactFiberOffscreenComponent';
4545
import checkPropTypes from 'shared/checkPropTypes';
4646
import {
4747
markComponentRenderStarted,
@@ -683,7 +683,9 @@ function updateOffscreenComponent(
683683

684684
if (
685685
nextProps.mode === 'hidden' ||
686-
(enableLegacyHidden && nextProps.mode === 'unstable-defer-without-hiding')
686+
(enableLegacyHidden &&
687+
nextProps.mode === 'unstable-defer-without-hiding') ||
688+
workInProgress.stateNode._visibility & OffscreenDetached
687689
) {
688690
// Rendering a hidden tree.
689691

packages/react-reconciler/src/ReactFiberBeginWork.old.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import {
4141
enableCPUSuspense,
4242
enableUseMutableSource,
4343
} from 'shared/ReactFeatureFlags';
44-
44+
import {OffscreenDetached} from './ReactFiberOffscreenComponent';
4545
import checkPropTypes from 'shared/checkPropTypes';
4646
import {
4747
markComponentRenderStarted,
@@ -683,7 +683,9 @@ function updateOffscreenComponent(
683683

684684
if (
685685
nextProps.mode === 'hidden' ||
686-
(enableLegacyHidden && nextProps.mode === 'unstable-defer-without-hiding')
686+
(enableLegacyHidden &&
687+
nextProps.mode === 'unstable-defer-without-hiding') ||
688+
workInProgress.stateNode._visibility & OffscreenDetached
687689
) {
688690
// Rendering a hidden tree.
689691

packages/react-reconciler/src/ReactFiberCommitWork.new.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import type {
3030
import type {HookFlags} from './ReactHookEffectTags';
3131
import type {Cache} from './ReactFiberCacheComponent.new';
3232
import type {RootState} from './ReactFiberRoot.new';
33+
import {scheduleMicrotask} from './ReactFiberHostConfig';
3334
import type {
3435
Transition,
3536
TracingMarkerInstance,
@@ -154,6 +155,7 @@ import {
154155
setIsRunningInsertionEffect,
155156
getExecutionContext,
156157
CommitContext,
158+
RenderContext,
157159
NoContext,
158160
} from './ReactFiberWorkLoop.new';
159161
import {
@@ -182,6 +184,7 @@ import {releaseCache, retainCache} from './ReactFiberCacheComponent.new';
182184
import {clearTransitionsForLanes} from './ReactFiberLane.new';
183185
import {
184186
OffscreenVisible,
187+
OffscreenDetached,
185188
OffscreenPassiveEffectsConnected,
186189
} from './ReactFiberOffscreenComponent';
187190
import {
@@ -1078,7 +1081,9 @@ function commitLayoutEffectOnFiber(
10781081
case OffscreenComponent: {
10791082
const isModernRoot = (finishedWork.mode & ConcurrentMode) !== NoMode;
10801083
if (isModernRoot) {
1081-
const isHidden = finishedWork.memoizedState !== null;
1084+
const isHidden =
1085+
finishedWork.memoizedState !== null ||
1086+
finishedWork.stateNode._visibility & OffscreenDetached;
10821087
const newOffscreenSubtreeIsHidden =
10831088
isHidden || offscreenSubtreeIsHidden;
10841089
if (newOffscreenSubtreeIsHidden) {
@@ -2255,6 +2260,23 @@ function getRetryCache(finishedWork) {
22552260
}
22562261
}
22572262

2263+
function attachOffscreenActions(offscreenFiber: Fiber, root: FiberRoot) {
2264+
offscreenFiber.stateNode.detach = () => {
2265+
const executionContext = getExecutionContext();
2266+
if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
2267+
scheduleMicrotask(() => {
2268+
offscreenFiber.stateNode._visibility |= OffscreenDetached;
2269+
disappearLayoutEffects(offscreenFiber);
2270+
disconnectPassiveEffect(offscreenFiber);
2271+
});
2272+
} else {
2273+
offscreenFiber.stateNode._visibility |= OffscreenDetached;
2274+
disappearLayoutEffects(offscreenFiber);
2275+
disconnectPassiveEffect(offscreenFiber);
2276+
}
2277+
};
2278+
}
2279+
22582280
function attachSuspenseRetryListeners(
22592281
finishedWork: Fiber,
22602282
wakeables: Set<Wakeable>,
@@ -2633,6 +2655,7 @@ function commitMutationEffectsOnFiber(
26332655
}
26342656

26352657
commitReconciliationEffects(finishedWork);
2658+
attachOffscreenActions(finishedWork, root);
26362659

26372660
if (flags & Visibility) {
26382661
const offscreenInstance: OffscreenInstance = finishedWork.stateNode;
@@ -2659,7 +2682,10 @@ function commitMutationEffectsOnFiber(
26592682
}
26602683
}
26612684

2662-
if (supportsMutation) {
2685+
if (
2686+
supportsMutation &&
2687+
!(offscreenInstance._visibility & OffscreenDetached)
2688+
) {
26632689
// TODO: This needs to run whenever there's an insertion or update
26642690
// inside a hidden Offscreen tree.
26652691
hideOrUnhideAllChildren(offscreenBoundary, isHidden);

packages/react-reconciler/src/ReactFiberCommitWork.old.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import type {
3030
import type {HookFlags} from './ReactHookEffectTags';
3131
import type {Cache} from './ReactFiberCacheComponent.old';
3232
import type {RootState} from './ReactFiberRoot.old';
33+
import {scheduleMicrotask} from './ReactFiberHostConfig';
3334
import type {
3435
Transition,
3536
TracingMarkerInstance,
@@ -154,6 +155,7 @@ import {
154155
setIsRunningInsertionEffect,
155156
getExecutionContext,
156157
CommitContext,
158+
RenderContext,
157159
NoContext,
158160
} from './ReactFiberWorkLoop.old';
159161
import {
@@ -182,6 +184,7 @@ import {releaseCache, retainCache} from './ReactFiberCacheComponent.old';
182184
import {clearTransitionsForLanes} from './ReactFiberLane.old';
183185
import {
184186
OffscreenVisible,
187+
OffscreenDetached,
185188
OffscreenPassiveEffectsConnected,
186189
} from './ReactFiberOffscreenComponent';
187190
import {
@@ -1078,7 +1081,9 @@ function commitLayoutEffectOnFiber(
10781081
case OffscreenComponent: {
10791082
const isModernRoot = (finishedWork.mode & ConcurrentMode) !== NoMode;
10801083
if (isModernRoot) {
1081-
const isHidden = finishedWork.memoizedState !== null;
1084+
const isHidden =
1085+
finishedWork.memoizedState !== null ||
1086+
finishedWork.stateNode._visibility & OffscreenDetached;
10821087
const newOffscreenSubtreeIsHidden =
10831088
isHidden || offscreenSubtreeIsHidden;
10841089
if (newOffscreenSubtreeIsHidden) {
@@ -2255,6 +2260,23 @@ function getRetryCache(finishedWork) {
22552260
}
22562261
}
22572262

2263+
function attachOffscreenActions(offscreenFiber: Fiber, root: FiberRoot) {
2264+
offscreenFiber.stateNode.detach = () => {
2265+
const executionContext = getExecutionContext();
2266+
if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
2267+
scheduleMicrotask(() => {
2268+
offscreenFiber.stateNode._visibility |= OffscreenDetached;
2269+
disappearLayoutEffects(offscreenFiber);
2270+
disconnectPassiveEffect(offscreenFiber);
2271+
});
2272+
} else {
2273+
offscreenFiber.stateNode._visibility |= OffscreenDetached;
2274+
disappearLayoutEffects(offscreenFiber);
2275+
disconnectPassiveEffect(offscreenFiber);
2276+
}
2277+
};
2278+
}
2279+
22582280
function attachSuspenseRetryListeners(
22592281
finishedWork: Fiber,
22602282
wakeables: Set<Wakeable>,
@@ -2633,6 +2655,7 @@ function commitMutationEffectsOnFiber(
26332655
}
26342656

26352657
commitReconciliationEffects(finishedWork);
2658+
attachOffscreenActions(finishedWork, root);
26362659

26372660
if (flags & Visibility) {
26382661
const offscreenInstance: OffscreenInstance = finishedWork.stateNode;
@@ -2659,7 +2682,10 @@ function commitMutationEffectsOnFiber(
26592682
}
26602683
}
26612684

2662-
if (supportsMutation) {
2685+
if (
2686+
supportsMutation &&
2687+
!(offscreenInstance._visibility & OffscreenDetached)
2688+
) {
26632689
// TODO: This needs to run whenever there's an insertion or update
26642690
// inside a hidden Offscreen tree.
26652691
hideOrUnhideAllChildren(offscreenBoundary, isHidden);

packages/react-reconciler/src/ReactFiberCompleteWork.new.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import type {
2828
SuspenseListRenderState,
2929
} from './ReactFiberSuspenseComponent.new';
3030
import type {OffscreenState} from './ReactFiberOffscreenComponent';
31+
import {OffscreenDetached} from './ReactFiberOffscreenComponent';
3132
import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent.new';
3233
import type {Cache} from './ReactFiberCacheComponent.new';
3334
import {
@@ -410,7 +411,15 @@ if (supportsMutation) {
410411
if (child !== null) {
411412
child.return = node;
412413
}
413-
appendAllChildrenToContainer(containerChildSet, node, true, true);
414+
// Detached tree is hidden from user space.
415+
const _needsVisibilityToggle =
416+
(node.stateNode._visibility & OffscreenDetached) === 0;
417+
appendAllChildrenToContainer(
418+
containerChildSet,
419+
node,
420+
_needsVisibilityToggle,
421+
true,
422+
);
414423
} else if (node.child !== null) {
415424
node.child.return = node;
416425
node = node.child;

packages/react-reconciler/src/ReactFiberCompleteWork.old.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import type {
2828
SuspenseListRenderState,
2929
} from './ReactFiberSuspenseComponent.old';
3030
import type {OffscreenState} from './ReactFiberOffscreenComponent';
31+
import {OffscreenDetached} from './ReactFiberOffscreenComponent';
3132
import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent.old';
3233
import type {Cache} from './ReactFiberCacheComponent.old';
3334
import {
@@ -410,7 +411,15 @@ if (supportsMutation) {
410411
if (child !== null) {
411412
child.return = node;
412413
}
413-
appendAllChildrenToContainer(containerChildSet, node, true, true);
414+
// Detached tree is hidden from user space.
415+
const _needsVisibilityToggle =
416+
(node.stateNode._visibility & OffscreenDetached) === 0;
417+
appendAllChildrenToContainer(
418+
containerChildSet,
419+
node,
420+
_needsVisibilityToggle,
421+
true,
422+
);
414423
} else if (node.child !== null) {
415424
node.child.return = node;
416425
node = node.child;

packages/react-reconciler/src/ReactFiberOffscreenComponent.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,14 @@ export type OffscreenQueue = {
4444

4545
type OffscreenVisibility = number;
4646

47-
export const OffscreenVisible = /* */ 0b01;
48-
export const OffscreenPassiveEffectsConnected = /* */ 0b10;
47+
export const OffscreenVisible = /* */ 0b001;
48+
export const OffscreenDetached = /* */ 0b010;
49+
export const OffscreenPassiveEffectsConnected = /* */ 0b100;
4950

5051
export type OffscreenInstance = {
5152
_visibility: OffscreenVisibility,
5253
_pendingMarkers: Set<TracingMarkerInstance> | null,
5354
_transitions: Set<Transition> | null,
5455
_retryCache: WeakSet<Wakeable> | Set<Wakeable> | null,
56+
detach: () => void,
5557
};

packages/react-reconciler/src/ReactFiberWorkLoop.new.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ type ExecutionContext = number;
277277

278278
export const NoContext = /* */ 0b000;
279279
const BatchedContext = /* */ 0b001;
280-
const RenderContext = /* */ 0b010;
280+
export const RenderContext = /* */ 0b010;
281281
export const CommitContext = /* */ 0b100;
282282

283283
type RootExitStatus = 0 | 1 | 2 | 3 | 4 | 5 | 6;

0 commit comments

Comments
 (0)