Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add detach to Offscreen component #25265

Merged
merged 17 commits into from
Oct 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/react-reconciler/src/ReactFiber.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ import {
} from './ReactWorkTags';
import {OffscreenVisible} from './ReactFiberOffscreenComponent';
import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';

import {isDevToolsPresent} from './ReactFiberDevToolsHook.new';
import {
resolveClassForHotReloading,
Expand Down Expand Up @@ -109,6 +108,7 @@ import {
REACT_TRACING_MARKER_TYPE,
} from 'shared/ReactSymbols';
import {TransitionTracingMarker} from './ReactFiberTracingMarkerComponent.new';
import {detachOffscreenInstance} from './ReactFiberCommitWork.new';

export type {Fiber};

Expand Down Expand Up @@ -755,6 +755,8 @@ export function createFiberFromOffscreen(
_pendingMarkers: null,
_retryCache: null,
_transitions: null,
_current: null,
detach: () => detachOffscreenInstance(primaryChildInstance),
};
fiber.stateNode = primaryChildInstance;
return fiber;
Expand All @@ -776,6 +778,8 @@ export function createFiberFromLegacyHidden(
_pendingMarkers: null,
_transitions: null,
_retryCache: null,
_current: null,
detach: () => detachOffscreenInstance(instance),
};
fiber.stateNode = instance;
return fiber;
Expand Down
6 changes: 5 additions & 1 deletion packages/react-reconciler/src/ReactFiber.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ import {
} from './ReactWorkTags';
import {OffscreenVisible} from './ReactFiberOffscreenComponent';
import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';

import {isDevToolsPresent} from './ReactFiberDevToolsHook.old';
import {
resolveClassForHotReloading,
Expand Down Expand Up @@ -109,6 +108,7 @@ import {
REACT_TRACING_MARKER_TYPE,
} from 'shared/ReactSymbols';
import {TransitionTracingMarker} from './ReactFiberTracingMarkerComponent.old';
import {detachOffscreenInstance} from './ReactFiberCommitWork.old';

export type {Fiber};

Expand Down Expand Up @@ -755,6 +755,8 @@ export function createFiberFromOffscreen(
_pendingMarkers: null,
_retryCache: null,
_transitions: null,
_current: null,
detach: () => detachOffscreenInstance(primaryChildInstance),
};
fiber.stateNode = primaryChildInstance;
return fiber;
Expand All @@ -776,6 +778,8 @@ export function createFiberFromLegacyHidden(
_pendingMarkers: null,
_transitions: null,
_retryCache: null,
_current: null,
detach: () => detachOffscreenInstance(instance),
};
fiber.stateNode = instance;
return fiber;
Expand Down
7 changes: 5 additions & 2 deletions packages/react-reconciler/src/ReactFiberBeginWork.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import type {
OffscreenQueue,
OffscreenInstance,
} from './ReactFiberOffscreenComponent';
import {OffscreenDetached} from './ReactFiberOffscreenComponent';
import type {
Cache,
CacheComponentState,
Expand All @@ -37,7 +38,6 @@ import type {
import type {UpdateQueue} from './ReactFiberClassUpdateQueue.new';
import type {RootState} from './ReactFiberRoot.new';
import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent.new';

import checkPropTypes from 'shared/checkPropTypes';
import {
markComponentRenderStarted,
Expand Down Expand Up @@ -688,7 +688,10 @@ function updateOffscreenComponent(

if (
nextProps.mode === 'hidden' ||
(enableLegacyHidden && nextProps.mode === 'unstable-defer-without-hiding')
(enableLegacyHidden &&
nextProps.mode === 'unstable-defer-without-hiding') ||
// TODO: remove read from stateNode.
workInProgress.stateNode._visibility & OffscreenDetached
sammy-SC marked this conversation as resolved.
Show resolved Hide resolved
) {
// Rendering a hidden tree.

Expand Down
7 changes: 5 additions & 2 deletions packages/react-reconciler/src/ReactFiberBeginWork.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import type {
OffscreenQueue,
OffscreenInstance,
} from './ReactFiberOffscreenComponent';
import {OffscreenDetached} from './ReactFiberOffscreenComponent';
import type {
Cache,
CacheComponentState,
Expand All @@ -37,7 +38,6 @@ import type {
import type {UpdateQueue} from './ReactFiberClassUpdateQueue.old';
import type {RootState} from './ReactFiberRoot.old';
import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent.old';

import checkPropTypes from 'shared/checkPropTypes';
import {
markComponentRenderStarted,
Expand Down Expand Up @@ -688,7 +688,10 @@ function updateOffscreenComponent(

if (
nextProps.mode === 'hidden' ||
(enableLegacyHidden && nextProps.mode === 'unstable-defer-without-hiding')
(enableLegacyHidden &&
nextProps.mode === 'unstable-defer-without-hiding') ||
// TODO: remove read from stateNode.
workInProgress.stateNode._visibility & OffscreenDetached
) {
// Rendering a hidden tree.

Expand Down
31 changes: 30 additions & 1 deletion packages/react-reconciler/src/ReactFiberCommitWork.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import type {SuspenseState} from './ReactFiberSuspenseComponent.new';
import type {UpdateQueue} from './ReactFiberClassUpdateQueue.new';
import type {FunctionComponentUpdateQueue} from './ReactFiberHooks.new';
import type {Wakeable} from 'shared/ReactTypes';
import {isOffscreenManual} from './ReactFiberOffscreenComponent';
import type {
OffscreenState,
OffscreenInstance,
Expand Down Expand Up @@ -153,6 +154,7 @@ import {
clearSingleton,
acquireSingletonInstance,
releaseSingletonInstance,
scheduleMicrotask,
} from './ReactFiberHostConfig';
import {
captureCommitPhaseError,
Expand All @@ -169,6 +171,7 @@ import {
setIsRunningInsertionEffect,
getExecutionContext,
CommitContext,
RenderContext,
NoContext,
} from './ReactFiberWorkLoop.new';
import {
Expand Down Expand Up @@ -197,6 +200,7 @@ import {releaseCache, retainCache} from './ReactFiberCacheComponent.new';
import {clearTransitionsForLanes} from './ReactFiberLane.new';
import {
OffscreenVisible,
OffscreenDetached,
OffscreenPassiveEffectsConnected,
} from './ReactFiberOffscreenComponent';
import {
Expand Down Expand Up @@ -2426,6 +2430,28 @@ function getRetryCache(finishedWork) {
}
}

export function detachOffscreenInstance(instance: OffscreenInstance): void {
const currentOffscreenFiber = instance._current;
if (currentOffscreenFiber === null) {
throw new Error(
'Calling Offscreen.detach before instance handle has been set.',
);
}

const executionContext = getExecutionContext();
if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
scheduleMicrotask(() => {
instance._visibility |= OffscreenDetached;
disappearLayoutEffects(currentOffscreenFiber);
disconnectPassiveEffect(currentOffscreenFiber);
});
} else {
instance._visibility |= OffscreenDetached;
disappearLayoutEffects(currentOffscreenFiber);
disconnectPassiveEffect(currentOffscreenFiber);
}
}

function attachSuspenseRetryListeners(
finishedWork: Fiber,
wakeables: Set<Wakeable>,
Expand Down Expand Up @@ -2842,6 +2868,8 @@ function commitMutationEffectsOnFiber(
}

commitReconciliationEffects(finishedWork);
// TODO: Add explicit effect flag to set _current.
finishedWork.stateNode._current = finishedWork;
sammy-SC marked this conversation as resolved.
Show resolved Hide resolved

if (flags & Visibility) {
const offscreenInstance: OffscreenInstance = finishedWork.stateNode;
Expand All @@ -2868,7 +2896,8 @@ function commitMutationEffectsOnFiber(
}
}

if (supportsMutation) {
// Offscreen with manual mode manages visibility manually.
if (supportsMutation && !isOffscreenManual(finishedWork)) {
// TODO: This needs to run whenever there's an insertion or update
// inside a hidden Offscreen tree.
hideOrUnhideAllChildren(offscreenBoundary, isHidden);
Expand Down
31 changes: 30 additions & 1 deletion packages/react-reconciler/src/ReactFiberCommitWork.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import type {SuspenseState} from './ReactFiberSuspenseComponent.old';
import type {UpdateQueue} from './ReactFiberClassUpdateQueue.old';
import type {FunctionComponentUpdateQueue} from './ReactFiberHooks.old';
import type {Wakeable} from 'shared/ReactTypes';
import {isOffscreenManual} from './ReactFiberOffscreenComponent';
import type {
OffscreenState,
OffscreenInstance,
Expand Down Expand Up @@ -153,6 +154,7 @@ import {
clearSingleton,
acquireSingletonInstance,
releaseSingletonInstance,
scheduleMicrotask,
} from './ReactFiberHostConfig';
import {
captureCommitPhaseError,
Expand All @@ -169,6 +171,7 @@ import {
setIsRunningInsertionEffect,
getExecutionContext,
CommitContext,
RenderContext,
NoContext,
} from './ReactFiberWorkLoop.old';
import {
Expand Down Expand Up @@ -197,6 +200,7 @@ import {releaseCache, retainCache} from './ReactFiberCacheComponent.old';
import {clearTransitionsForLanes} from './ReactFiberLane.old';
import {
OffscreenVisible,
OffscreenDetached,
OffscreenPassiveEffectsConnected,
} from './ReactFiberOffscreenComponent';
import {
Expand Down Expand Up @@ -2426,6 +2430,28 @@ function getRetryCache(finishedWork) {
}
}

export function detachOffscreenInstance(instance: OffscreenInstance): void {
const currentOffscreenFiber = instance._current;
if (currentOffscreenFiber === null) {
throw new Error(
'Calling Offscreen.detach before instance handle has been set.',
);
}

const executionContext = getExecutionContext();
if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
scheduleMicrotask(() => {
instance._visibility |= OffscreenDetached;
disappearLayoutEffects(currentOffscreenFiber);
disconnectPassiveEffect(currentOffscreenFiber);
});
} else {
instance._visibility |= OffscreenDetached;
disappearLayoutEffects(currentOffscreenFiber);
disconnectPassiveEffect(currentOffscreenFiber);
}
}

function attachSuspenseRetryListeners(
finishedWork: Fiber,
wakeables: Set<Wakeable>,
Expand Down Expand Up @@ -2842,6 +2868,8 @@ function commitMutationEffectsOnFiber(
}

commitReconciliationEffects(finishedWork);
// TODO: Add explicit effect flag to set _current.
finishedWork.stateNode._current = finishedWork;

if (flags & Visibility) {
const offscreenInstance: OffscreenInstance = finishedWork.stateNode;
Expand All @@ -2868,7 +2896,8 @@ function commitMutationEffectsOnFiber(
}
}

if (supportsMutation) {
// Offscreen with manual mode manages visibility manually.
if (supportsMutation && !isOffscreenManual(finishedWork)) {
// TODO: This needs to run whenever there's an insertion or update
// inside a hidden Offscreen tree.
hideOrUnhideAllChildren(offscreenBoundary, isHidden);
Expand Down
10 changes: 9 additions & 1 deletion packages/react-reconciler/src/ReactFiberCompleteWork.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import type {
SuspenseState,
SuspenseListRenderState,
} from './ReactFiberSuspenseComponent.new';
import {isOffscreenManual} from './ReactFiberOffscreenComponent';
import type {OffscreenState} from './ReactFiberOffscreenComponent';
import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent.new';
import type {Cache} from './ReactFiberCacheComponent.new';
Expand Down Expand Up @@ -428,7 +429,14 @@ if (supportsMutation) {
if (child !== null) {
child.return = node;
}
appendAllChildrenToContainer(containerChildSet, node, true, true);
// If Offscreen is not in manual mode, detached tree is hidden from user space.
const _needsVisibilityToggle = !isOffscreenManual(node);
appendAllChildrenToContainer(
containerChildSet,
node,
_needsVisibilityToggle,
true,
);
} else if (node.child !== null) {
node.child.return = node;
node = node.child;
Expand Down
10 changes: 9 additions & 1 deletion packages/react-reconciler/src/ReactFiberCompleteWork.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import type {
SuspenseState,
SuspenseListRenderState,
} from './ReactFiberSuspenseComponent.old';
import {isOffscreenManual} from './ReactFiberOffscreenComponent';
import type {OffscreenState} from './ReactFiberOffscreenComponent';
import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent.old';
import type {Cache} from './ReactFiberCacheComponent.old';
Expand Down Expand Up @@ -428,7 +429,14 @@ if (supportsMutation) {
if (child !== null) {
child.return = node;
}
appendAllChildrenToContainer(containerChildSet, node, true, true);
// If Offscreen is not in manual mode, detached tree is hidden from user space.
const _needsVisibilityToggle = !isOffscreenManual(node);
appendAllChildrenToContainer(
containerChildSet,
node,
_needsVisibilityToggle,
true,
);
} else if (node.child !== null) {
node.child.return = node;
node = node.child;
Expand Down
19 changes: 17 additions & 2 deletions packages/react-reconciler/src/ReactFiberOffscreenComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import type {ReactNodeList, OffscreenMode, Wakeable} from 'shared/ReactTypes';
import type {Lanes} from './ReactFiberLane.old';
import type {SpawnedCachePool} from './ReactFiberCacheComponent.new';
import type {Fiber} from './ReactInternalTypes';
import type {
Transition,
TracingMarkerInstance,
Expand Down Expand Up @@ -44,13 +45,27 @@ export type OffscreenQueue = {

type OffscreenVisibility = number;

export const OffscreenVisible = /* */ 0b01;
export const OffscreenPassiveEffectsConnected = /* */ 0b10;
export const OffscreenVisible = /* */ 0b001;
export const OffscreenDetached = /* */ 0b010;
export const OffscreenPassiveEffectsConnected = /* */ 0b100;

export type OffscreenInstance = {
_visibility: OffscreenVisibility,
_pendingMarkers: Set<TracingMarkerInstance> | null,
_transitions: Set<Transition> | null,
// $FlowFixMe[incompatible-type-arg] found when upgrading Flow
_retryCache: WeakSet<Wakeable> | Set<Wakeable> | null,

// Represents the current Offscreen fiber
_current: Fiber | null,
detach: () => void,

// TODO: attach
};

export function isOffscreenManual(offscreenFiber: Fiber): boolean {
return (
offscreenFiber.memoizedProps !== null &&
offscreenFiber.memoizedProps.mode === 'manual'
);
}
2 changes: 1 addition & 1 deletion packages/react-reconciler/src/ReactFiberWorkLoop.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ type ExecutionContext = number;

export const NoContext = /* */ 0b000;
const BatchedContext = /* */ 0b001;
const RenderContext = /* */ 0b010;
export const RenderContext = /* */ 0b010;
export const CommitContext = /* */ 0b100;

type RootExitStatus = 0 | 1 | 2 | 3 | 4 | 5 | 6;
Expand Down
2 changes: 1 addition & 1 deletion packages/react-reconciler/src/ReactFiberWorkLoop.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ type ExecutionContext = number;

export const NoContext = /* */ 0b000;
const BatchedContext = /* */ 0b001;
const RenderContext = /* */ 0b010;
export const RenderContext = /* */ 0b010;
export const CommitContext = /* */ 0b100;

type RootExitStatus = 0 | 1 | 2 | 3 | 4 | 5 | 6;
Expand Down
Loading