diff --git a/packages/react-reconciler/src/ReactFiber.js b/packages/react-reconciler/src/ReactFiber.js index 7f5fbe59b8..614df0831f 100644 --- a/packages/react-reconciler/src/ReactFiber.js +++ b/packages/react-reconciler/src/ReactFiber.js @@ -110,10 +110,6 @@ import { REACT_ACTIVITY_TYPE, } from 'shared/ReactSymbols'; import {TransitionTracingMarker} from './ReactFiberTracingMarkerComponent'; -import { - detachOffscreenInstance, - attachOffscreenInstance, -} from './ReactFiberCommitWork'; import {getHostContext} from './ReactFiberHostContext'; import type {ReactComponentInfo} from '../../shared/ReactTypes'; import isArray from 'shared/isArray'; @@ -854,13 +850,9 @@ export function createFiberFromOffscreen( fiber.lanes = lanes; const primaryChildInstance: OffscreenInstance = { _visibility: OffscreenVisible, - _pendingVisibility: OffscreenVisible, _pendingMarkers: null, _retryCache: null, _transitions: null, - _current: null, - detach: () => detachOffscreenInstance(primaryChildInstance), - attach: () => attachOffscreenInstance(primaryChildInstance), }; fiber.stateNode = primaryChildInstance; return fiber; @@ -909,13 +901,9 @@ export function createFiberFromLegacyHidden( // the offscreen implementation, which depends on a state node const instance: OffscreenInstance = { _visibility: OffscreenVisible, - _pendingVisibility: OffscreenVisible, _pendingMarkers: null, _transitions: null, _retryCache: null, - _current: null, - detach: () => detachOffscreenInstance(instance), - attach: () => attachOffscreenInstance(instance), }; fiber.stateNode = instance; return fiber; diff --git a/packages/react-reconciler/src/ReactFiberActivityComponent.js b/packages/react-reconciler/src/ReactFiberActivityComponent.js index 48b5bc0e6d..8d9dcad67e 100644 --- a/packages/react-reconciler/src/ReactFiberActivityComponent.js +++ b/packages/react-reconciler/src/ReactFiberActivityComponent.js @@ -10,7 +10,6 @@ import type {ReactNodeList, OffscreenMode, Wakeable} from 'shared/ReactTypes'; import type {Lanes} from './ReactFiberLane'; import type {SpawnedCachePool} from './ReactFiberCacheComponent'; -import type {Fiber} from './ReactInternalTypes'; import type { Transition, TracingMarkerInstance, @@ -47,25 +46,11 @@ export type OffscreenQueue = { type OffscreenVisibility = number; export const OffscreenVisible = /* */ 0b001; -export const OffscreenDetached = /* */ 0b010; -export const OffscreenPassiveEffectsConnected = /* */ 0b100; +export const OffscreenPassiveEffectsConnected = /* */ 0b010; export type OffscreenInstance = { - _pendingVisibility: OffscreenVisibility, _visibility: OffscreenVisibility, _pendingMarkers: Set | null, _transitions: Set | null, _retryCache: WeakSet | Set | null, - - // Represents the current Offscreen fiber - _current: Fiber | null, - detach: () => void, - attach: () => void, }; - -export function isOffscreenManual(offscreenFiber: Fiber): boolean { - return ( - offscreenFiber.memoizedProps !== null && - offscreenFiber.memoizedProps.mode === 'manual' - ); -} diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index 4636c08818..a5ff0d1757 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -33,7 +33,6 @@ import type { ViewTransitionState, } from './ReactFiberViewTransitionComponent'; import {assignViewTransitionAutoName} from './ReactFiberViewTransitionComponent'; -import {OffscreenDetached} from './ReactFiberActivityComponent'; import type { Cache, CacheComponentState, @@ -647,19 +646,13 @@ function updateOffscreenComponent( ) { const nextProps: OffscreenProps = workInProgress.pendingProps; const nextChildren = nextProps.children; - const nextIsDetached = - (workInProgress.stateNode._pendingVisibility & OffscreenDetached) !== 0; const prevState: OffscreenState | null = current !== null ? current.memoizedState : null; - markRef(current, workInProgress); - if ( nextProps.mode === 'hidden' || - (enableLegacyHidden && - nextProps.mode === 'unstable-defer-without-hiding') || - nextIsDetached + (enableLegacyHidden && nextProps.mode === 'unstable-defer-without-hiding') ) { // Rendering a hidden tree. diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.js b/packages/react-reconciler/src/ReactFiberCommitWork.js index 63bf7b7eb9..e73b3fa8fb 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.js @@ -18,20 +18,15 @@ import type { } from './ReactFiberConfig'; import type {Fiber, FiberRoot} from './ReactInternalTypes'; import type {Lanes} from './ReactFiberLane'; -import { - includesOnlyViewTransitionEligibleLanes, - SyncLane, -} from './ReactFiberLane'; +import {includesOnlyViewTransitionEligibleLanes} from './ReactFiberLane'; import type {SuspenseState, RetryQueue} from './ReactFiberSuspenseComponent'; import type {UpdateQueue} from './ReactFiberClassUpdateQueue'; import type {FunctionComponentUpdateQueue} from './ReactFiberHooks'; import type {Wakeable} from 'shared/ReactTypes'; -import {isOffscreenManual} from './ReactFiberActivityComponent'; import type { OffscreenState, OffscreenInstance, OffscreenQueue, - OffscreenProps, } from './ReactFiberActivityComponent'; import type {Cache} from './ReactFiberCacheComponent'; import type {RootState} from './ReactFiberRoot'; @@ -194,15 +189,12 @@ import {releaseCache, retainCache} from './ReactFiberCacheComponent'; import {clearTransitionsForLanes} from './ReactFiberLane'; import { OffscreenVisible, - OffscreenDetached, OffscreenPassiveEffectsConnected, } from './ReactFiberActivityComponent'; import { TransitionRoot, TransitionTracingMarker, } from './ReactFiberTracingMarkerComponent'; -import {scheduleUpdateOnFiber} from './ReactFiberWorkLoop'; -import {enqueueConcurrentRenderForLane} from './ReactFiberConcurrentUpdates'; import { commitHookLayoutEffects, commitHookLayoutUnmountEffects, @@ -742,14 +734,6 @@ function commitLayoutEffectOnFiber( committedLanes, ); } - if (flags & Ref) { - const props: OffscreenProps = finishedWork.memoizedProps; - if (props.mode === 'manual') { - safelyAttachRef(finishedWork, finishedWork.return); - } else { - safelyDetachRef(finishedWork, finishedWork.return); - } - } break; } case ViewTransitionComponent: { @@ -1538,9 +1522,6 @@ function commitDeletionEffectsOnFiber( return; } case OffscreenComponent: { - if (!offscreenSubtreeWasHidden) { - safelyDetachRef(deletedFiber, nearestMountedAncestor); - } if (disableLegacyMode || deletedFiber.mode & ConcurrentMode) { // If this offscreen component is hidden, we already unmounted it. Before // deleting the children, track that it's already unmounted so that we @@ -1672,48 +1653,6 @@ function getRetryCache(finishedWork: Fiber) { } } -export function detachOffscreenInstance(instance: OffscreenInstance): void { - const fiber = instance._current; - if (fiber === null) { - throw new Error( - 'Calling Offscreen.detach before instance handle has been set.', - ); - } - - if ((instance._pendingVisibility & OffscreenDetached) !== NoFlags) { - // The instance is already detached, this is a noop. - return; - } - - // TODO: There is an opportunity to optimise this by not entering commit phase - // and unmounting effects directly. - const root = enqueueConcurrentRenderForLane(fiber, SyncLane); - if (root !== null) { - instance._pendingVisibility |= OffscreenDetached; - scheduleUpdateOnFiber(root, fiber, SyncLane); - } -} - -export function attachOffscreenInstance(instance: OffscreenInstance): void { - const fiber = instance._current; - if (fiber === null) { - throw new Error( - 'Calling Offscreen.detach before instance handle has been set.', - ); - } - - if ((instance._pendingVisibility & OffscreenDetached) === NoFlags) { - // The instance is already attached, this is a noop. - return; - } - - const root = enqueueConcurrentRenderForLane(fiber, SyncLane); - if (root !== null) { - instance._pendingVisibility &= ~OffscreenDetached; - scheduleUpdateOnFiber(root, fiber, SyncLane); - } -} - function attachSuspenseRetryListeners( finishedWork: Fiber, wakeables: RetryQueue, @@ -2181,12 +2120,6 @@ function commitMutationEffectsOnFiber( break; } case OffscreenComponent: { - if (flags & Ref) { - if (!offscreenSubtreeWasHidden && current !== null) { - safelyDetachRef(current, current.return); - } - } - const newState: OffscreenState | null = finishedWork.memoizedState; const isHidden = newState !== null; const wasHidden = current !== null && current.memoizedState !== null; @@ -2208,18 +2141,9 @@ function commitMutationEffectsOnFiber( commitReconciliationEffects(finishedWork, lanes); - const offscreenInstance: OffscreenInstance = finishedWork.stateNode; - - // TODO: Add explicit effect flag to set _current. - offscreenInstance._current = finishedWork; - - // Offscreen stores pending changes to visibility in `_pendingVisibility`. This is - // to support batching of `attach` and `detach` calls. - offscreenInstance._visibility &= ~OffscreenDetached; - offscreenInstance._visibility |= - offscreenInstance._pendingVisibility & OffscreenDetached; - if (flags & Visibility) { + const offscreenInstance: OffscreenInstance = finishedWork.stateNode; + // Track the current state on the Offscreen instance so we can // read it during an event if (isHidden) { @@ -2250,8 +2174,7 @@ function commitMutationEffectsOnFiber( } } - // Offscreen with manual mode manages visibility manually. - if (supportsMutation && !isOffscreenManual(finishedWork)) { + if (supportsMutation) { // TODO: This needs to run whenever there's an insertion or update // inside a hidden Offscreen tree. hideOrUnhideAllChildren(finishedWork, isHidden); @@ -2667,9 +2590,6 @@ export function disappearLayoutEffects(finishedWork: Fiber) { break; } case OffscreenComponent: { - // TODO (Offscreen) Check: flags & RefStatic - safelyDetachRef(finishedWork, finishedWork.return); - const isHidden = finishedWork.memoizedState !== null; if (isHidden) { // Nested Offscreen tree is already hidden. Don't disappear diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.js b/packages/react-reconciler/src/ReactFiberCompleteWork.js index ce11278275..663523dc95 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.js @@ -28,7 +28,6 @@ import type { OffscreenState, OffscreenQueue, } from './ReactFiberActivityComponent'; -import {isOffscreenManual} from './ReactFiberActivityComponent'; import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent'; import type {Cache} from './ReactFiberCacheComponent'; import { @@ -384,12 +383,10 @@ function appendAllChildrenToContainer( if (child !== null) { child.return = node; } - // If Offscreen is not in manual mode, detached tree is hidden from user space. - const _needsVisibilityToggle = !isOffscreenManual(node); appendAllChildrenToContainer( containerChildSet, node, - /* needsVisibilityToggle */ _needsVisibilityToggle, + /* needsVisibilityToggle */ true, /* isHidden */ true, ); diff --git a/packages/react-reconciler/src/__tests__/Activity-test.js b/packages/react-reconciler/src/__tests__/Activity-test.js index 191e72e3cb..4655d4917d 100644 --- a/packages/react-reconciler/src/__tests__/Activity-test.js +++ b/packages/react-reconciler/src/__tests__/Activity-test.js @@ -9,7 +9,6 @@ let useLayoutEffect; let useEffect; let useInsertionEffect; let useMemo; -let useRef; let startTransition; let waitForPaint; let waitFor; @@ -31,7 +30,6 @@ describe('Activity', () => { useLayoutEffect = React.useLayoutEffect; useEffect = React.useEffect; useMemo = React.useMemo; - useRef = React.useRef; startTransition = React.startTransition; const InternalTestUtils = require('internal-test-utils'); @@ -46,30 +44,6 @@ describe('Activity', () => { return {props.children}; } - function LoggedText({text, children}) { - useInsertionEffect(() => { - Scheduler.log(`mount insertion ${text}`); - return () => { - Scheduler.log(`unmount insertion ${text}`); - }; - }); - - useEffect(() => { - Scheduler.log(`mount ${text}`); - return () => { - Scheduler.log(`unmount ${text}`); - }; - }); - - useLayoutEffect(() => { - Scheduler.log(`mount layout ${text}`); - return () => { - Scheduler.log(`unmount layout ${text}`); - }; - }); - return {children}; - } - // @gate enableLegacyHidden it('unstable-defer-without-hiding should never toggle the visibility of its children', async () => { function App({mode}) { @@ -1506,641 +1480,4 @@ describe('Activity', () => { assertLog([]); expect(root).toMatchRenderedOutput(); }); - - describe('manual interactivity', () => { - // @gate enableActivity - it('should attach ref only for mode null', async () => { - let offscreenRef; - - function App({mode}) { - offscreenRef = useRef(null); - return ( - { - offscreenRef.current = ref; - }}> -
- - ); - } - - const root = ReactNoop.createRoot(); - - await act(() => { - root.render(); - }); - - expect(offscreenRef.current).not.toBeNull(); - - await act(() => { - root.render(); - }); - - expect(offscreenRef.current).toBeNull(); - - await act(() => { - root.render(); - }); - - expect(offscreenRef.current).toBeNull(); - - await act(() => { - root.render(); - }); - - expect(offscreenRef.current).not.toBeNull(); - }); - - // @gate enableActivity - it('should lower update priority for detached Activity', async () => { - let updateChildState; - let updateHighPriorityComponentState; - let offscreenRef; - - function Child() { - const [state, _stateUpdate] = useState(0); - updateChildState = _stateUpdate; - const text = 'Child ' + state; - return ; - } - - function HighPriorityComponent(props) { - const [state, _stateUpdate] = useState(0); - updateHighPriorityComponentState = _stateUpdate; - const text = 'HighPriorityComponent ' + state; - return ( - <> - - {props.children} - - ); - } - - function App() { - offscreenRef = useRef(null); - return ( - <> - - - - - - - ); - } - - const root = ReactNoop.createRoot(); - - await act(() => { - root.render(); - }); - - assertLog(['HighPriorityComponent 0', 'Child 0']); - expect(root).toMatchRenderedOutput( - <> - - - , - ); - - expect(offscreenRef.current).not.toBeNull(); - - // Activity is attached by default. State updates from offscreen are **not defered**. - await act(async () => { - updateChildState(1); - updateHighPriorityComponentState(1); - await waitForPaint(['HighPriorityComponent 1', 'Child 1']); - expect(root).toMatchRenderedOutput( - <> - - - , - ); - }); - - await act(() => { - offscreenRef.current.detach(); - }); - - // Activity is detached. State updates from offscreen are **defered**. - await act(async () => { - updateChildState(2); - updateHighPriorityComponentState(2); - await waitForPaint(['HighPriorityComponent 2']); - expect(root).toMatchRenderedOutput( - <> - - - , - ); - }); - - assertLog(['Child 2']); - expect(root).toMatchRenderedOutput( - <> - - - , - ); - - await act(() => { - offscreenRef.current.attach(); - }); - - // Activity is attached. State updates from offscreen are **not defered**. - await act(async () => { - updateChildState(3); - updateHighPriorityComponentState(3); - await waitForPaint(['HighPriorityComponent 3', 'Child 3']); - expect(root).toMatchRenderedOutput( - <> - - - , - ); - }); - }); - - // @gate enableActivity - it('defers detachment if called during commit', async () => { - let updateChildState; - let updateHighPriorityComponentState; - let offscreenRef; - let nextRenderTriggerDetach = false; - let nextRenderTriggerAttach = false; - - function Child() { - const [state, _stateUpdate] = useState(0); - updateChildState = _stateUpdate; - const text = 'Child ' + state; - return ; - } - - function HighPriorityComponent(props) { - const [state, _stateUpdate] = useState(0); - updateHighPriorityComponentState = _stateUpdate; - const text = 'HighPriorityComponent ' + state; - useLayoutEffect(() => { - if (nextRenderTriggerDetach) { - _stateUpdate(state + 1); - updateChildState(state + 1); - offscreenRef.current.detach(); - nextRenderTriggerDetach = false; - } - - if (nextRenderTriggerAttach) { - offscreenRef.current.attach(); - nextRenderTriggerAttach = false; - } - }); - return ( - <> - - {props.children} - - ); - } - - function App() { - offscreenRef = useRef(null); - return ( - <> - - - - - - - ); - } - - const root = ReactNoop.createRoot(); - - await act(() => { - root.render(); - }); - - assertLog(['HighPriorityComponent 0', 'Child 0']); - - nextRenderTriggerDetach = true; - - // Activity is attached and gets detached inside useLayoutEffect. - // State updates from offscreen are **defered**. - await act(async () => { - updateChildState(1); - updateHighPriorityComponentState(1); - await waitForPaint([ - 'HighPriorityComponent 1', - 'Child 1', - 'HighPriorityComponent 2', - ]); - expect(root).toMatchRenderedOutput( - <> - - - , - ); - }); - - assertLog(['Child 2']); - expect(root).toMatchRenderedOutput( - <> - - - , - ); - - nextRenderTriggerAttach = true; - - // Activity is detached. State updates from offscreen are **defered**. - // Activity is attached inside useLayoutEffect; - await act(async () => { - updateChildState(3); - updateHighPriorityComponentState(3); - await waitForPaint(['HighPriorityComponent 3', 'Child 3']); - expect(root).toMatchRenderedOutput( - <> - - - , - ); - }); - }); - }); - - // @gate enableActivity - it('should detach ref if Activity is unmounted', async () => { - let offscreenRef; - - function App({showOffscreen}) { - offscreenRef = useRef(null); - return showOffscreen ? ( - { - offscreenRef.current = ref; - }}> -
- - ) : null; - } - - const root = ReactNoop.createRoot(); - - await act(() => { - root.render(); - }); - - expect(offscreenRef.current).not.toBeNull(); - - await act(() => { - root.render(); - }); - - expect(offscreenRef.current).toBeNull(); - - await act(() => { - root.render(); - }); - - expect(offscreenRef.current).not.toBeNull(); - }); - - // @gate enableActivity - it('should detach ref when parent Activity is hidden', async () => { - let offscreenRef; - - function App({mode}) { - offscreenRef = useRef(null); - return ( - - -
- - - ); - } - - const root = ReactNoop.createRoot(); - - await act(() => { - root.render(); - }); - - expect(offscreenRef.current).toBeNull(); - - await act(() => { - root.render(); - }); - - expect(offscreenRef.current).not.toBeNull(); - await act(() => { - root.render(); - }); - - expect(offscreenRef.current).toBeNull(); - }); - - // @gate enableActivity - it('should change _current', async () => { - let offscreenRef; - const root = ReactNoop.createRoot(); - - function App({children}) { - offscreenRef = useRef(null); - return ( - - {children} - - ); - } - - await act(() => { - root.render( - -
- , - ); - }); - - expect(offscreenRef.current).not.toBeNull(); - const firstFiber = offscreenRef.current._current; - - await act(() => { - root.render( - - - , - ); - }); - - expect(offscreenRef.current._current === firstFiber).toBeFalsy(); - }); - - // @gate enableActivity - it('does not mount tree until attach is called', async () => { - let offscreenRef; - let spanRef; - - function Child() { - spanRef = useRef(null); - useEffect(() => { - Scheduler.log('Mount Child'); - return () => { - Scheduler.log('Unmount Child'); - }; - }); - useLayoutEffect(() => { - Scheduler.log('Mount Layout Child'); - return () => { - Scheduler.log('Unmount Layout Child'); - }; - }); - - return Child; - } - - function App() { - return ( - (offscreenRef = el)}> - - - ); - } - - const root = ReactNoop.createRoot(); - - await act(() => { - root.render(); - }); - - expect(offscreenRef).not.toBeNull(); - expect(spanRef.current).not.toBeNull(); - assertLog(['Mount Layout Child', 'Mount Child']); - - await act(() => { - offscreenRef.detach(); - }); - - expect(spanRef.current).toBeNull(); - assertLog(['Unmount Layout Child', 'Unmount Child']); - - // Calling attach on already attached Activity. - await act(() => { - offscreenRef.detach(); - }); - - assertLog([]); - - await act(() => { - offscreenRef.attach(); - }); - - expect(spanRef.current).not.toBeNull(); - assertLog(['Mount Layout Child', 'Mount Child']); - - // Calling attach on already attached Activity - offscreenRef.attach(); - - assertLog([]); - }); - - // @gate enableActivity - it('handles nested manual offscreens', async () => { - let outerOffscreen; - let innerOffscreen; - - function App() { - return ( - - (outerOffscreen = el)}> - - (innerOffscreen = el)}> - - - - - - ); - } - - const root = ReactNoop.createRoot(); - - await act(() => { - root.render(); - }); - - assertLog([ - 'outer', - 'middle', - 'inner', - 'mount insertion inner', - 'mount insertion middle', - 'mount insertion outer', - 'mount layout inner', - 'mount layout middle', - 'mount layout outer', - 'mount inner', - 'mount middle', - 'mount outer', - ]); - - expect(outerOffscreen).not.toBeNull(); - expect(innerOffscreen).not.toBeNull(); - - await act(() => { - outerOffscreen.detach(); - }); - - expect(innerOffscreen).toBeNull(); - - assertLog([ - 'unmount layout middle', - 'unmount layout inner', - 'unmount middle', - 'unmount inner', - ]); - - await act(() => { - outerOffscreen.attach(); - }); - - assertLog([ - 'mount layout inner', - 'mount layout middle', - 'mount inner', - 'mount middle', - ]); - - await act(() => { - innerOffscreen.detach(); - }); - - assertLog(['unmount layout inner', 'unmount inner']); - - // Calling detach on already detached Activity. - await act(() => { - innerOffscreen.detach(); - }); - - assertLog([]); - - await act(() => { - innerOffscreen.attach(); - }); - - assertLog(['mount layout inner', 'mount inner']); - - await act(() => { - innerOffscreen.detach(); - outerOffscreen.attach(); - }); - - assertLog(['unmount layout inner', 'unmount inner']); - - await act(() => { - root.render(null); - }); - - assertLog([ - 'unmount insertion outer', - 'unmount layout outer', - 'unmount insertion middle', - 'unmount layout middle', - ...(gate('enableHiddenSubtreeInsertionEffectCleanup') - ? ['unmount insertion inner'] - : []), - 'unmount outer', - 'unmount middle', - ]); - }); - - // @gate enableActivity - it('batches multiple attach and detach calls scheduled from an event handler', async () => { - function Child() { - useEffect(() => { - Scheduler.log('attach child'); - return () => { - Scheduler.log('detach child'); - }; - }, []); - return 'child'; - } - - const offscreen = React.createRef(null); - function App() { - return ( - - - - ); - } - - const root = ReactNoop.createRoot(); - await act(() => { - root.render(); - }); - - assertLog(['attach child']); - - await act(() => { - const instance = offscreen.current; - // Detach then immediately attach the instance. - instance.detach(); - instance.attach(); - }); - - assertLog([]); - - await act(() => { - const instance = offscreen.current; - instance.detach(); - }); - - assertLog(['detach child']); - - await act(() => { - const instance = offscreen.current; - // Attach then immediately detach. - instance.attach(); - instance.detach(); - }); - - assertLog([]); - }); - - // @gate enableActivity - it('batches multiple attach and detach calls scheduled from an effect', async () => { - function Child() { - useEffect(() => { - Scheduler.log('attach child'); - return () => { - Scheduler.log('detach child'); - }; - }, []); - return 'child'; - } - - function App() { - const offscreen = useRef(null); - useLayoutEffect(() => { - const instance = offscreen.current; - // Detach then immediately attach the instance. - instance.detach(); - instance.attach(); - }, []); - return ( - - - - ); - } - - const root = ReactNoop.createRoot(); - await act(() => { - root.render(); - }); - assertLog(['attach child']); - }); }); diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index 521575a504..fbbd62f3af 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -140,8 +140,7 @@ export type Thenable = export type OffscreenMode = | 'hidden' | 'unstable-defer-without-hiding' - | 'visible' - | 'manual'; + | 'visible'; export type StartTransitionOptions = { name?: string,