From 54fdb2f71810ef78c355108c219822ba097de8b3 Mon Sep 17 00:00:00 2001 From: Rick Hanlon Date: Mon, 3 Mar 2025 17:34:35 -0500 Subject: [PATCH] [internal] add activity mode for legacy hidden --- .../src/ReactFiberActivityComponent.js | 9 + .../src/ReactFiberBeginWork.js | 3 + .../src/ReactFiberCommitWork.js | 156 ++++++++++++++++-- .../src/ReactFiberCompleteWork.js | 39 ++++- .../src/ReactFiberConcurrentUpdates.js | 7 +- .../src/ReactFiberSuspenseContext.js | 7 +- .../react-reconciler/src/ReactFiberThrow.js | 4 + .../src/ReactFiberWorkLoop.js | 6 +- .../src/__tests__/ActivitySuspense-test.js | 5 +- packages/react-server/src/ReactFizzServer.js | 13 ++ packages/react/src/ReactClient.js | 18 +- packages/shared/ReactFeatureFlags.js | 2 + packages/shared/ReactTypes.js | 8 +- .../ReactFeatureFlags.native-fb-dynamic.js | 1 + .../forks/ReactFeatureFlags.native-fb.js | 1 + .../forks/ReactFeatureFlags.native-oss.js | 1 + .../forks/ReactFeatureFlags.test-renderer.js | 1 + ...actFeatureFlags.test-renderer.native-fb.js | 2 + .../ReactFeatureFlags.test-renderer.www.js | 2 + .../forks/ReactFeatureFlags.www-dynamic.js | 2 + .../shared/forks/ReactFeatureFlags.www.js | 1 + 21 files changed, 259 insertions(+), 29 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberActivityComponent.js b/packages/react-reconciler/src/ReactFiberActivityComponent.js index 48b5bc0e6d8ee..ed2adff3b9473 100644 --- a/packages/react-reconciler/src/ReactFiberActivityComponent.js +++ b/packages/react-reconciler/src/ReactFiberActivityComponent.js @@ -69,3 +69,12 @@ export function isOffscreenManual(offscreenFiber: Fiber): boolean { offscreenFiber.memoizedProps.mode === 'manual' ); } + +export function isLegacyHiddenMode(fiber: Fiber): boolean { + return ( + fiber != null && + fiber.memoizedProps != null && + (fiber.memoizedProps.mode === 'unstable-legacy-hidden' || + fiber.memoizedProps.mode === 'unstable-defer-without-hiding') + ); +} diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index bea69b2063de2..a99dcd9acef2f 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -304,6 +304,7 @@ import { callComponentInDEV, callRenderInDEV, } from './ReactFiberCallUserSpace'; +import {enableReplaceLegacyHiddenWithActivity} from 'shared/ReactFeatureFlags'; // A special exception that's used to unwind the stack when an update flows // into a dehydrated boundary. @@ -659,6 +660,8 @@ function updateOffscreenComponent( nextProps.mode === 'hidden' || (enableLegacyHidden && nextProps.mode === 'unstable-defer-without-hiding') || + (enableReplaceLegacyHiddenWithActivity && + nextProps.mode === 'unstable-legacy-hidden') || nextIsDetached ) { // Rendering a hidden tree. diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.js b/packages/react-reconciler/src/ReactFiberCommitWork.js index f5d0987393046..6f3f054c89ff6 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.js @@ -26,7 +26,10 @@ 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 { + isLegacyHiddenMode, + isOffscreenManual, +} from './ReactFiberActivityComponent'; import type { OffscreenState, OffscreenInstance, @@ -61,6 +64,7 @@ import { disableLegacyMode, enableComponentPerformanceTrack, enableViewTransition, + enableReplaceLegacyHiddenWithActivity, } from 'shared/ReactFeatureFlags'; import { FunctionComponent, @@ -700,6 +704,18 @@ function commitLayoutEffectOnFiber( break; } case OffscreenComponent: { + if ( + enableReplaceLegacyHiddenWithActivity && + (isLegacyHiddenMode(finishedWork) || isLegacyHiddenMode(current)) + ) { + recursivelyTraverseLayoutEffects( + finishedRoot, + finishedWork, + committedLanes, + ); + break; + } + const isModernRoot = disableLegacyMode || (finishedWork.mode & ConcurrentMode) !== NoMode; if (isModernRoot) { @@ -1534,6 +1550,17 @@ function commitDeletionEffectsOnFiber( return; } case OffscreenComponent: { + if ( + enableReplaceLegacyHiddenWithActivity && + isLegacyHiddenMode(deletedFiber) + ) { + recursivelyTraverseDeletionEffects( + finishedRoot, + nearestMountedAncestor, + deletedFiber, + ); + break; + } if (!offscreenSubtreeWasHidden) { safelyDetachRef(deletedFiber, nearestMountedAncestor); } @@ -1643,13 +1670,20 @@ function getRetryCache(finishedWork: Fiber) { return retryCache; } case OffscreenComponent: { - const instance: OffscreenInstance = finishedWork.stateNode; - let retryCache: null | Set | WeakSet = - instance._retryCache; - if (retryCache === null) { - retryCache = instance._retryCache = new PossiblyWeakSet(); + if ( + enableReplaceLegacyHiddenWithActivity && + isLegacyHiddenMode(finishedWork) + ) { + // Fall through to error + } else { + const instance: OffscreenInstance = finishedWork.stateNode; + let retryCache: null | Set | WeakSet = + instance._retryCache; + if (retryCache === null) { + retryCache = instance._retryCache = new PossiblyWeakSet(); + } + return retryCache; } - return retryCache; } default: { throw new Error( @@ -2168,6 +2202,14 @@ function commitMutationEffectsOnFiber( break; } case OffscreenComponent: { + if ( + enableReplaceLegacyHiddenWithActivity && + (isLegacyHiddenMode(finishedWork) || isLegacyHiddenMode(current)) + ) { + recursivelyTraverseMutationEffects(root, finishedWork, lanes); + commitReconciliationEffects(finishedWork, lanes); + break; + } if (flags & Ref) { if (!offscreenSubtreeWasHidden && current !== null) { safelyDetachRef(current, current.return); @@ -2473,6 +2515,13 @@ function commitAfterMutationEffectsOnFiber( break; } case OffscreenComponent: { + if ( + enableReplaceLegacyHiddenWithActivity && + (isLegacyHiddenMode(finishedWork) || isLegacyHiddenMode(current)) + ) { + recursivelyTraverseAfterMutationEffects(root, finishedWork, lanes); + break; + } const isModernRoot = disableLegacyMode || (finishedWork.mode & ConcurrentMode) !== NoMode; if (isModernRoot) { @@ -2645,6 +2694,14 @@ export function disappearLayoutEffects(finishedWork: Fiber) { break; } case OffscreenComponent: { + if ( + enableReplaceLegacyHiddenWithActivity && + isLegacyHiddenMode(finishedWork) + ) { + recursivelyTraverseDisappearLayoutEffects(finishedWork); + break; + } + // TODO (Offscreen) Check: flags & RefStatic safelyDetachRef(finishedWork, finishedWork.return); @@ -2835,6 +2892,17 @@ export function reappearLayoutEffects( break; } case OffscreenComponent: { + if ( + enableReplaceLegacyHiddenWithActivity && + isLegacyHiddenMode(finishedWork) + ) { + recursivelyTraverseReappearLayoutEffects( + finishedRoot, + finishedWork, + includeWorkInProgressEffects, + ); + break; + } const offscreenState: OffscreenState = finishedWork.memoizedState; const isHidden = offscreenState !== null; if (isHidden) { @@ -3463,6 +3531,27 @@ function commitPassiveMountOnFiber( break; } case OffscreenComponent: { + if ( + enableReplaceLegacyHiddenWithActivity && + (isLegacyHiddenMode(finishedWork) || + isLegacyHiddenMode(finishedWork.alternate)) + ) { + recursivelyTraversePassiveMountEffects( + finishedRoot, + finishedWork, + committedLanes, + committedTransitions, + endTime, + ); + + if (flags & Passive) { + const current = finishedWork.alternate; + const instance: OffscreenInstance = finishedWork.stateNode; + commitOffscreenPassiveMountEffects(current, finishedWork, instance); + } + break; + } + // TODO: Pass `current` as argument to this function const instance: OffscreenInstance = finishedWork.stateNode; const current = finishedWork.alternate; @@ -3780,6 +3869,21 @@ export function reconnectPassiveEffects( break; } case OffscreenComponent: { + if ( + (enableReplaceLegacyHiddenWithActivity && + isLegacyHiddenMode(finishedWork)) || + isLegacyHiddenMode(finishedWork.alternate) + ) { + recursivelyTraverseReconnectPassiveEffects( + finishedRoot, + finishedWork, + committedLanes, + committedTransitions, + includeWorkInProgressEffects, + endTime, + ); + break; + } const instance: OffscreenInstance = finishedWork.stateNode; const nextState: OffscreenState | null = finishedWork.memoizedState; @@ -3995,11 +4099,16 @@ function commitAtomicPassiveEffects( committedTransitions, endTime, ); - if (flags & Passive) { - // TODO: Pass `current` as argument to this function - const current = finishedWork.alternate; - const instance: OffscreenInstance = finishedWork.stateNode; - commitOffscreenPassiveMountEffects(current, finishedWork, instance); + // TODO: Pass `current` as argument to this function + const current = finishedWork.alternate; + if ( + !enableReplaceLegacyHiddenWithActivity || + (!isLegacyHiddenMode(finishedWork) && !isLegacyHiddenMode(current)) + ) { + if (flags & Passive) { + const instance: OffscreenInstance = finishedWork.stateNode; + commitOffscreenPassiveMountEffects(current, finishedWork, instance); + } } break; } @@ -4110,6 +4219,13 @@ function accumulateSuspenseyCommitOnFiber(fiber: Fiber) { break; } case OffscreenComponent: { + if ( + enableReplaceLegacyHiddenWithActivity && + (isLegacyHiddenMode(fiber) || isLegacyHiddenMode(fiber.alternate)) + ) { + recursivelyAccumulateSuspenseyCommit(fiber); + break; + } const isHidden = (fiber.memoizedState: OffscreenState | null) !== null; if (isHidden) { // Don't suspend in hidden trees @@ -4257,6 +4373,14 @@ function commitPassiveUnmountOnFiber(finishedWork: Fiber): void { break; } case OffscreenComponent: { + if ( + (enableReplaceLegacyHiddenWithActivity && + isLegacyHiddenMode(finishedWork)) || + isLegacyHiddenMode(finishedWork.alternate) + ) { + recursivelyTraversePassiveUnmountEffects(finishedWork); + break; + } const instance: OffscreenInstance = finishedWork.stateNode; const nextState: OffscreenState | null = finishedWork.memoizedState; @@ -4355,6 +4479,14 @@ export function disconnectPassiveEffect(finishedWork: Fiber): void { break; } case OffscreenComponent: { + if ( + enableReplaceLegacyHiddenWithActivity && + (isLegacyHiddenMode(finishedWork) || + isLegacyHiddenMode(finishedWork.alternate)) + ) { + recursivelyTraverseDisconnectPassiveEffects(finishedWork); + break; + } const instance: OffscreenInstance = finishedWork.stateNode; if (instance._visibility & OffscreenPassiveEffectsConnected) { instance._visibility &= ~OffscreenPassiveEffectsConnected; diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.js b/packages/react-reconciler/src/ReactFiberCompleteWork.js index dc8e9a1bffa01..3b50fdab0a260 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.js @@ -28,7 +28,10 @@ import type { OffscreenState, OffscreenQueue, } from './ReactFiberActivityComponent'; -import {isOffscreenManual} from './ReactFiberActivityComponent'; +import { + isLegacyHiddenMode, + isOffscreenManual, +} from './ReactFiberActivityComponent'; import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent'; import type {Cache} from './ReactFiberCacheComponent'; import { @@ -180,6 +183,7 @@ import { popRootMarkerInstance, } from './ReactFiberTracingMarkerComponent'; import {suspendCommit} from './ReactFiberThenable'; +import {enableReplaceLegacyHiddenWithActivity} from 'shared/forks/ReactFeatureFlags.native-fb-dynamic'; /** * Tag the fiber with an update effect. This turns a Placement into @@ -299,7 +303,8 @@ function appendAllChildren( // the portal directly. } else if ( node.tag === OffscreenComponent && - node.memoizedState !== null + node.memoizedState !== null && + !isLegacyHiddenMode(node) ) { // The children in this boundary are hidden. Toggle their visibility // before appending. @@ -371,7 +376,8 @@ function appendAllChildrenToContainer( // the portal directly. } else if ( node.tag === OffscreenComponent && - node.memoizedState !== null + node.memoizedState !== null && + !isLegacyHiddenMode(node) ) { // The children in this boundary are hidden. Toggle their visibility // before appending. @@ -619,7 +625,8 @@ function scheduleRetryEffect( // TODO: This check should probably be moved into claimNextRetryLane // I also suspect that we need some further consolidation of offscreen // and retry lanes. - workInProgress.tag !== OffscreenComponent + workInProgress.tag !== OffscreenComponent || + isLegacyHiddenMode(workInProgress) ? claimNextRetryLane() : OffscreenLane; workInProgress.lanes = mergeLanes(workInProgress.lanes, retryLane); @@ -1755,10 +1762,18 @@ function completeWork( const nextState: OffscreenState | null = workInProgress.memoizedState; const nextIsHidden = nextState !== null; - // Schedule a Visibility effect if the visibility has changed - if (enableLegacyHidden && workInProgress.tag === LegacyHiddenComponent) { + if ( + enableReplaceLegacyHiddenWithActivity && + isLegacyHiddenMode(workInProgress) + ) { + // LegacyHidden doesn't do any hiding — it only pre-renders. + } else if ( + enableLegacyHidden && + workInProgress.tag === LegacyHiddenComponent + ) { // LegacyHidden doesn't do any hiding — it only pre-renders. } else { + // Schedule a Visibility effect if the visibility has changed if (current !== null) { const prevState: OffscreenState | null = current.memoizedState; const prevIsHidden = prevState !== null; @@ -1793,10 +1808,16 @@ function completeWork( // If so, we need to hide those nodes in the commit phase, so // schedule a visibility effect. if ( - (!enableLegacyHidden || - workInProgress.tag !== LegacyHiddenComponent) && - workInProgress.subtreeFlags & (Placement | Update) + enableLegacyHidden && + workInProgress.tag === LegacyHiddenComponent + ) { + // LegacyHidden doesn't do any hiding — it only pre-renders. + } else if ( + enableReplaceLegacyHiddenWithActivity && + isLegacyHiddenMode(workInProgress) ) { + // LegacyHidden doesn't do any hiding — it only pre-renders. + } else if (workInProgress.subtreeFlags & (Placement | Update)) { workInProgress.flags |= Visibility; } } diff --git a/packages/react-reconciler/src/ReactFiberConcurrentUpdates.js b/packages/react-reconciler/src/ReactFiberConcurrentUpdates.js index 859e553ccbcb6..61edb8cee6c3c 100644 --- a/packages/react-reconciler/src/ReactFiberConcurrentUpdates.js +++ b/packages/react-reconciler/src/ReactFiberConcurrentUpdates.js @@ -34,7 +34,10 @@ import { } from './ReactFiberLane'; import {NoFlags, Placement, Hydrating} from './ReactFiberFlags'; import {HostRoot, OffscreenComponent} from './ReactWorkTags'; -import {OffscreenVisible} from './ReactFiberActivityComponent'; +import { + isLegacyHiddenMode, + OffscreenVisible, +} from './ReactFiberActivityComponent'; export type ConcurrentUpdate = { next: ConcurrentUpdate, @@ -233,7 +236,7 @@ function markUpdateLaneFromFiberToRoot( alternate.childLanes = mergeLanes(alternate.childLanes, lane); } - if (parent.tag === OffscreenComponent) { + if (parent.tag === OffscreenComponent && !isLegacyHiddenMode(parent)) { // Check if this offscreen boundary is currently hidden. // // The instance may be null if the Offscreen parent was unmounted. Usually diff --git a/packages/react-reconciler/src/ReactFiberSuspenseContext.js b/packages/react-reconciler/src/ReactFiberSuspenseContext.js index a03b45356a1fc..ed2a4ef552409 100644 --- a/packages/react-reconciler/src/ReactFiberSuspenseContext.js +++ b/packages/react-reconciler/src/ReactFiberSuspenseContext.js @@ -16,6 +16,8 @@ import {enableSuspenseAvoidThisFallback} from 'shared/ReactFeatureFlags'; import {createCursor, push, pop} from './ReactFiberStack'; import {isCurrentTreeHidden} from './ReactFiberHiddenContext'; import {OffscreenComponent} from './ReactWorkTags'; +import {isLegacyHiddenMode} from './ReactFiberActivityComponent'; +import {enableReplaceLegacyHiddenWithActivity} from 'shared/forks/ReactFeatureFlags.native-fb-dynamic'; // The Suspense handler is the boundary that should capture if something // suspends, i.e. it's the nearest `catch` block on the stack. @@ -107,7 +109,10 @@ export function pushFallbackTreeSuspenseHandler(fiber: Fiber): void { } export function pushOffscreenSuspenseHandler(fiber: Fiber): void { - if (fiber.tag === OffscreenComponent) { + if ( + fiber.tag === OffscreenComponent && + (!enableReplaceLegacyHiddenWithActivity || !isLegacyHiddenMode(fiber)) + ) { // A SuspenseList context is only pushed here to avoid a push/pop mismatch. // Reuse the current value on the stack. // TODO: We can avoid needing to push here by by forking popSuspenseHandler diff --git a/packages/react-reconciler/src/ReactFiberThrow.js b/packages/react-reconciler/src/ReactFiberThrow.js index a777331699082..2669735a8b735 100644 --- a/packages/react-reconciler/src/ReactFiberThrow.js +++ b/packages/react-reconciler/src/ReactFiberThrow.js @@ -86,6 +86,7 @@ import {noopSuspenseyCommitThenable} from './ReactFiberThenable'; import {REACT_POSTPONE_TYPE} from 'shared/ReactSymbols'; import {runWithFiberInDEV} from './ReactCurrentFiber'; import {callComponentDidCatchInDEV} from './ReactFiberCallUserSpace'; +import {isLegacyHiddenMode} from './ReactFiberActivityComponent'; function createRootErrorUpdate( root: FiberRoot, @@ -484,6 +485,9 @@ function throwException( return false; } case OffscreenComponent: { + if (isLegacyHiddenMode(suspenseBoundary)) { + // Fall through to error. + } if (disableLegacyMode || suspenseBoundary.mode & ConcurrentMode) { suspenseBoundary.flags |= ShouldCapture; const isSuspenseyResource = diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index bf611f73275a7..6278a09bcbb7e 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -351,6 +351,7 @@ import { deleteScheduledGesture, stopCompletedGestures, } from './ReactFiberGestureScheduler'; +import {isLegacyHiddenMode} from './ReactFiberActivityComponent'; const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map; @@ -4442,6 +4443,9 @@ export function resolveRetryWakeable(boundaryFiber: Fiber, wakeable: Wakeable) { retryCache = boundaryFiber.stateNode; break; case OffscreenComponent: { + if (isLegacyHiddenMode(boundaryFiber)) { + // Fall through to error. + } const instance: OffscreenInstance = boundaryFiber.stateNode; retryCache = instance._retryCache; break; @@ -4560,7 +4564,7 @@ function doubleInvokeEffectsInDEVIfNecessary( // First case: the fiber **is not** of type OffscreenComponent. No // special rules apply to double invoking effects. - if (fiber.tag !== OffscreenComponent) { + if (fiber.tag !== OffscreenComponent || isLegacyHiddenMode(fiber)) { if (fiber.flags & PlacementDEV) { if (isInStrictMode) { runWithFiberInDEV( diff --git a/packages/react-reconciler/src/__tests__/ActivitySuspense-test.js b/packages/react-reconciler/src/__tests__/ActivitySuspense-test.js index 2fb35df4574ad..bb78f0c435e26 100644 --- a/packages/react-reconciler/src/__tests__/ActivitySuspense-test.js +++ b/packages/react-reconciler/src/__tests__/ActivitySuspense-test.js @@ -141,9 +141,12 @@ describe('Activity Suspense', () => { test('LegacyHidden does not handle suspense', async () => { const root = ReactNoop.createRoot(); + function Fallback() { + return ; + } function App() { return ( - }> + }> diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index cc4173486404b..560f74e3ef1ad 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -175,6 +175,7 @@ import getComponentNameFromType from 'shared/getComponentNameFromType'; import isArray from 'shared/isArray'; import {SuspenseException, getSuspendedThenable} from './ReactFizzThenable'; import type {Postpone} from 'react/src/ReactPostpone'; +import {enableReplaceLegacyHiddenWithActivity} from 'shared/forks/ReactFeatureFlags.native-fb-dynamic'; // Linked list representing the identity of a component given the component/tag name and key. // The name might be minified but we assume that it's going to be the same generated name. Typically @@ -2264,6 +2265,18 @@ function renderElement( } case REACT_ACTIVITY_TYPE: case REACT_OFFSCREEN_TYPE: { + if ( + enableReplaceLegacyHiddenWithActivity && + (props.mode === 'unstable-legacy-hidden' || + props.mode === 'unstable-defer-without-hiding') + ) { + // Always render hidden content for legacy hidden modes. + const prevKeyPath = task.keyPath; + task.keyPath = keyPath; + renderNodeDestructive(request, task, props.children, -1); + task.keyPath = prevKeyPath; + return; + } renderOffscreen(request, task, keyPath, props); return; } diff --git a/packages/react/src/ReactClient.js b/packages/react/src/ReactClient.js index b48a3cdb1a74e..813cdc53aed9e 100644 --- a/packages/react/src/ReactClient.js +++ b/packages/react/src/ReactClient.js @@ -65,6 +65,7 @@ import {addTransitionType} from './ReactTransitionType'; import {act} from './ReactAct'; import {captureOwnerStack} from './ReactOwnerStack'; import * as ReactCompilerRuntime from './ReactCompilerRuntime'; +import {enableReplaceLegacyHiddenWithActivity} from 'shared/ReactFeatureFlags'; const Children = { map, @@ -115,7 +116,6 @@ export { startTransition, useDeferredValue, REACT_SUSPENSE_LIST_TYPE as unstable_SuspenseList, - REACT_LEGACY_HIDDEN_TYPE as unstable_LegacyHidden, REACT_ACTIVITY_TYPE as unstable_Activity, getCacheForType as unstable_getCacheForType, useCacheRefresh as unstable_useCacheRefresh, @@ -134,3 +134,19 @@ export { act, captureOwnerStack, }; + +let unstable_LegacyHidden; +if (enableReplaceLegacyHiddenWithActivity) { + const Activity = REACT_ACTIVITY_TYPE; + unstable_LegacyHidden = function LegacyHidden({mode, children}) { + return ( + + {children} + + ); + }; +} else { + unstable_LegacyHidden = REACT_LEGACY_HIDDEN_TYPE; +} + +export {unstable_LegacyHidden}; diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 1a2c260b5ba2d..1e9564eb4058c 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -267,3 +267,5 @@ export const enableUpdaterTracking = __PROFILE__; // Internal only. export const enableDO_NOT_USE_disableStrictPassiveEffect = false; + +export const enableReplaceLegacyHiddenWithActivity = false; diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index 521575a5041b4..42cc12c2aaea0 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -139,9 +139,13 @@ export type Thenable = export type OffscreenMode = | 'hidden' - | 'unstable-defer-without-hiding' | 'visible' - | 'manual'; + | 'manual' + // These two modes are not shipping and www only. + // This will SSR the content, and mount the effects when hydrating. + | 'unstable-legacy-hidden' + // This will SSR the content, defer rendering, but will not hide the content, or defer effects. + | 'unstable-defer-without-hiding'; export type StartTransitionOptions = { name?: string, diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js b/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js index 6825b2b608b9b..3878537ea6cb7 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js @@ -28,3 +28,4 @@ export const enableSiblingPrerendering = __VARIANT__; export const enableUseEffectCRUDOverload = __VARIANT__; export const enableFastAddPropertiesInDiffing = __VARIANT__; export const enableLazyPublicInstanceInFabric = __VARIANT__; +export const enableReplaceLegacyHiddenWithActivity = __VARIANT__; diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 8d72f54fc94cd..8899e6f101a75 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -30,6 +30,7 @@ export const { enableSiblingPrerendering, enableFastAddPropertiesInDiffing, enableLazyPublicInstanceInFabric, + enableReplaceLegacyHiddenWithActivity, } = dynamicFlags; // These two can be removed diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index ea29191274af1..d7045ad7f56f3 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -81,6 +81,7 @@ export const enableProfilerTimer = __PROFILE__; export const enableProfilerCommitHooks = __PROFILE__; export const enableProfilerNestedUpdatePhase = __PROFILE__; export const enableUpdaterTracking = __PROFILE__; +export const enableReplaceLegacyHiddenWithActivity = true; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 821a577026c32..984e37ff93191 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -91,6 +91,7 @@ export const disableDefaultPropsExceptForClasses = true; export const enableObjectFiber = false; export const enableOwnerStacks = true; +export const enableReplaceLegacyHiddenWithActivity = true; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js index c239f61b78b6b..f36d831ec1f0a 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js @@ -9,6 +9,7 @@ import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags'; import typeof * as ExportsType from './ReactFeatureFlags.test-renderer'; +import {enableReplaceLegacyHiddenWithActivity} from './ReactFeatureFlags.www'; export const alwaysThrottleRetries = false; export const disableClientCache = true; @@ -71,6 +72,7 @@ export const enableViewTransition = false; export const enableSwipeTransition = false; export const enableFastAddPropertiesInDiffing = false; export const enableLazyPublicInstanceInFabric = false; +export const enableReplaceLegacyHiddenWithActivity = true; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 443bed04b790f..6ca6823b1ec6f 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -9,6 +9,7 @@ import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags'; import typeof * as ExportsType from './ReactFeatureFlags.test-renderer.www'; +import {enableReplaceLegacyHiddenWithActivity} from './ReactFeatureFlags.www'; export const enableAsyncDebugInfo = false; export const enableSchedulingProfiler = false; @@ -86,6 +87,7 @@ export const enableViewTransition = false; export const enableSwipeTransition = false; export const enableFastAddPropertiesInDiffing = false; export const enableLazyPublicInstanceInFabric = false; +export const enableReplaceLegacyHiddenWithActivity = true; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js index e0f431fe7f10c..7451d39d1269b 100644 --- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js @@ -46,3 +46,5 @@ export const enableViewTransition = __VARIANT__; export const enableTrustedTypesIntegration = false; // You probably *don't* want to add more hardcoded ones. // Instead, try to add them above with the __VARIANT__ value. + +export const enableReplaceLegacyHiddenWithActivity = __VARIANT__; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index de1f2f316bb53..bcb1e64e84beb 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -37,6 +37,7 @@ export const { transitionLaneExpirationMs, enableFastAddPropertiesInDiffing, enableViewTransition, + enableReplaceLegacyHiddenWithActivity, } = dynamicFeatureFlags; // On WWW, __EXPERIMENTAL__ is used for a new modern build.