diff --git a/fixtures/view-transition/src/components/Page.js b/fixtures/view-transition/src/components/Page.js index 3de87a949e5c7..d39b9bf45f9ca 100644 --- a/fixtures/view-transition/src/components/Page.js +++ b/fixtures/view-transition/src/components/Page.js @@ -41,6 +41,12 @@ function Component() { transitions['enter-slide-right'] + ' ' + transitions['exit-slide-left'] }>

Slide In from Left, Slide Out to Right

+

+ +

); } diff --git a/packages/react-art/src/ReactFiberConfigART.js b/packages/react-art/src/ReactFiberConfigART.js index db4e8015df131..be0f1210974de 100644 --- a/packages/react-art/src/ReactFiberConfigART.js +++ b/packages/react-art/src/ReactFiberConfigART.js @@ -596,6 +596,14 @@ export function maySuspendCommit(type, props) { return false; } +export function maySuspendCommitOnUpdate(type, oldProps, newProps) { + return false; +} + +export function maySuspendCommitInSyncRender(type, props) { + return false; +} + export function preloadInstance(type, props) { // Return true to indicate it's already loaded return true; @@ -603,7 +611,7 @@ export function preloadInstance(type, props) { export function startSuspendingCommit() {} -export function suspendInstance(type, props) {} +export function suspendInstance(instance, type, props) {} export function suspendOnActiveViewTransition(container) {} diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index 8b7b552238caf..23056953aeb1d 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -103,6 +103,7 @@ import { disableLegacyMode, enableMoveBefore, disableCommentsAsDOMContainers, + enableSuspenseyImages, } from 'shared/ReactFeatureFlags'; import { HostComponent, @@ -145,6 +146,10 @@ export type Props = { is?: string, size?: number, multiple?: boolean, + src?: string, + srcSet?: string, + loading?: 'eager' | 'lazy', + onLoad?: (event: any) => void, ... }; type RawProps = { @@ -769,9 +774,9 @@ export function commitMount( // only need to assign one. And Safari just never triggers a new load event which means this technique // is already a noop regardless of which properties are assigned. We should revisit if browsers update // this heuristic in the future. - if ((newProps: any).src) { + if (newProps.src) { ((domElement: any): HTMLImageElement).src = (newProps: any).src; - } else if ((newProps: any).srcSet) { + } else if (newProps.srcSet) { ((domElement: any): HTMLImageElement).srcset = (newProps: any).srcSet; } return; @@ -4974,6 +4979,36 @@ export function isHostHoistableType( } export function maySuspendCommit(type: Type, props: Props): boolean { + if (!enableSuspenseyImages) { + return false; + } + // Suspensey images are the default, unless you opt-out of with either + // loading="lazy" or onLoad={...} which implies you're ok waiting. + return ( + type === 'img' && + props.src != null && + props.src !== '' && + props.onLoad == null && + props.loading !== 'lazy' + ); +} + +export function maySuspendCommitOnUpdate( + type: Type, + oldProps: Props, + newProps: Props, +): boolean { + return ( + maySuspendCommit(type, newProps) && + (newProps.src !== oldProps.src || newProps.srcSet !== oldProps.srcSet) + ); +} + +export function maySuspendCommitInSyncRender( + type: Type, + props: Props, +): boolean { + // TODO: Allow sync lanes to suspend too with an opt-in. return false; } @@ -4984,8 +5019,17 @@ export function mayResourceSuspendCommit(resource: Resource): boolean { ); } -export function preloadInstance(type: Type, props: Props): boolean { - return true; +export function preloadInstance( + instance: Instance, + type: Type, + props: Props, +): boolean { + // We don't need to preload Suspensey images because the browser will + // load them early once we set the src. + // If we return true here, we'll still get a suspendInstance call in the + // pre-commit phase to determine if we still need to decode the image or + // if was dropped from cache. This just avoids rendering Suspense fallback. + return !!(instance: any).complete; } export function preloadResource(resource: Resource): boolean { @@ -5022,8 +5066,38 @@ export function startSuspendingCommit(): void { }; } -export function suspendInstance(type: Type, props: Props): void { - return; +const SUSPENSEY_IMAGE_TIMEOUT = 500; + +export function suspendInstance( + instance: Instance, + type: Type, + props: Props, +): void { + if (!enableSuspenseyImages) { + return; + } + if (suspendedState === null) { + throw new Error( + 'Internal React Error: suspendedState null when it was expected to exists. Please report this as a React bug.', + ); + } + const state = suspendedState; + if ( + // $FlowFixMe[prop-missing] + typeof instance.decode === 'function' && + typeof setTimeout === 'function' + ) { + // If this browser supports decode() API, we use it to suspend waiting on the image. + // The loading should have already started at this point, so it should be enough to + // just call decode() which should also wait for the data to finish loading. + state.count++; + const ping = onUnsuspend.bind(state); + Promise.race([ + // $FlowFixMe[prop-missing] + instance.decode(), + new Promise(resolve => setTimeout(resolve, SUSPENSEY_IMAGE_TIMEOUT)), + ]).then(ping, ping); + } } export function suspendResource( diff --git a/packages/react-native-renderer/src/ReactFiberConfigFabric.js b/packages/react-native-renderer/src/ReactFiberConfigFabric.js index c3b8ac4d35d14..d0ddc5f7f0dc0 100644 --- a/packages/react-native-renderer/src/ReactFiberConfigFabric.js +++ b/packages/react-native-renderer/src/ReactFiberConfigFabric.js @@ -577,13 +577,36 @@ export function maySuspendCommit(type: Type, props: Props): boolean { return false; } -export function preloadInstance(type: Type, props: Props): boolean { +export function maySuspendCommitOnUpdate( + type: Type, + oldProps: Props, + newProps: Props, +): boolean { + return false; +} + +export function maySuspendCommitInSyncRender( + type: Type, + props: Props, +): boolean { + return false; +} + +export function preloadInstance( + instance: Instance, + type: Type, + props: Props, +): boolean { return true; } export function startSuspendingCommit(): void {} -export function suspendInstance(type: Type, props: Props): void {} +export function suspendInstance( + instance: Instance, + type: Type, + props: Props, +): void {} export function suspendOnActiveViewTransition(container: Container): void {} diff --git a/packages/react-native-renderer/src/ReactFiberConfigNative.js b/packages/react-native-renderer/src/ReactFiberConfigNative.js index b022d82d4e6c7..6ff7eb3b91a3b 100644 --- a/packages/react-native-renderer/src/ReactFiberConfigNative.js +++ b/packages/react-native-renderer/src/ReactFiberConfigNative.js @@ -735,14 +735,37 @@ export function maySuspendCommit(type: Type, props: Props): boolean { return false; } -export function preloadInstance(type: Type, props: Props): boolean { +export function maySuspendCommitOnUpdate( + type: Type, + oldProps: Props, + newProps: Props, +): boolean { + return false; +} + +export function maySuspendCommitInSyncRender( + type: Type, + props: Props, +): boolean { + return false; +} + +export function preloadInstance( + instance: Instance, + type: Type, + props: Props, +): boolean { // Return false to indicate it's already loaded return true; } export function startSuspendingCommit(): void {} -export function suspendInstance(type: Type, props: Props): void {} +export function suspendInstance( + instance: Instance, + type: Type, + props: Props, +): void {} export function suspendOnActiveViewTransition(container: Container): void {} diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js index 57d86c6df5a9d..fc3c2fd3cde24 100644 --- a/packages/react-noop-renderer/src/createReactNoop.js +++ b/packages/react-noop-renderer/src/createReactNoop.js @@ -320,7 +320,11 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { suspenseyCommitSubscription = null; } - function suspendInstance(type: string, props: Props): void { + function suspendInstance( + instance: Instance, + type: string, + props: Props, + ): void { const src = props.src; if (type === 'suspensey-thing' && typeof src === 'string') { // Attach a listener to the suspensey thing and create a subscription @@ -624,13 +628,33 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { return type === 'suspensey-thing' && typeof props.src === 'string'; }, + maySuspendCommitOnUpdate( + type: string, + oldProps: Props, + newProps: Props, + ): boolean { + // Asks whether it's possible for this combination of type and props + // to ever need to suspend. This is different from asking whether it's + // currently ready because even if it's ready now, it might get purged + // from the cache later. + return ( + type === 'suspensey-thing' && + typeof newProps.src === 'string' && + newProps.src !== oldProps.src + ); + }, + + maySuspendCommitInSyncRender(type: string, props: Props): boolean { + return true; + }, + mayResourceSuspendCommit(resource: mixed): boolean { throw new Error( 'Resources are not implemented for React Noop yet. This method should not be called', ); }, - preloadInstance(type: string, props: Props): boolean { + preloadInstance(instance: Instance, type: string, props: Props): boolean { if (type !== 'suspensey-thing' || typeof props.src !== 'string') { throw new Error('Attempted to preload unexpected instance: ' + type); } diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.js b/packages/react-reconciler/src/ReactFiberCommitWork.js index 3d6d05dbec9aa..01a6a30f3116d 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.js @@ -18,7 +18,10 @@ import type { } from './ReactFiberConfig'; import type {Fiber, FiberRoot} from './ReactInternalTypes'; import type {Lanes} from './ReactFiberLane'; -import {includesOnlyViewTransitionEligibleLanes} from './ReactFiberLane'; +import { + includesOnlySuspenseyCommitEligibleLanes, + includesOnlyViewTransitionEligibleLanes, +} from './ReactFiberLane'; import type {SuspenseState, RetryQueue} from './ReactFiberSuspenseComponent'; import type {UpdateQueue} from './ReactFiberClassUpdateQueue'; import type {FunctionComponentUpdateQueue} from './ReactFiberHooks'; @@ -160,6 +163,7 @@ import { mountHoistable, unmountHoistable, prepareToCommitHoistables, + maySuspendCommitInSyncRender, suspendInstance, suspendResource, resetFormInstance, @@ -4280,25 +4284,31 @@ export function commitPassiveUnmountEffects(finishedWork: Fiber): void { // ViewTransitions so that we know to also visit those to collect appearing // pairs. let suspenseyCommitFlag = ShouldSuspendCommit; -export function accumulateSuspenseyCommit(finishedWork: Fiber): void { +export function accumulateSuspenseyCommit( + finishedWork: Fiber, + committedLanes: Lanes, +): void { resetAppearingViewTransitions(); - accumulateSuspenseyCommitOnFiber(finishedWork); + accumulateSuspenseyCommitOnFiber(finishedWork, committedLanes); } -function recursivelyAccumulateSuspenseyCommit(parentFiber: Fiber): void { +function recursivelyAccumulateSuspenseyCommit( + parentFiber: Fiber, + committedLanes: Lanes, +): void { if (parentFiber.subtreeFlags & suspenseyCommitFlag) { let child = parentFiber.child; while (child !== null) { - accumulateSuspenseyCommitOnFiber(child); + accumulateSuspenseyCommitOnFiber(child, committedLanes); child = child.sibling; } } } -function accumulateSuspenseyCommitOnFiber(fiber: Fiber) { +function accumulateSuspenseyCommitOnFiber(fiber: Fiber, committedLanes: Lanes) { switch (fiber.tag) { case HostHoistable: { - recursivelyAccumulateSuspenseyCommit(fiber); + recursivelyAccumulateSuspenseyCommit(fiber, committedLanes); if (fiber.flags & suspenseyCommitFlag) { if (fiber.memoizedState !== null) { suspendResource( @@ -4308,19 +4318,33 @@ function accumulateSuspenseyCommitOnFiber(fiber: Fiber) { fiber.memoizedProps, ); } else { + const instance = fiber.stateNode; const type = fiber.type; const props = fiber.memoizedProps; - suspendInstance(type, props); + // TODO: Allow sync lanes to suspend too with an opt-in. + if ( + includesOnlySuspenseyCommitEligibleLanes(committedLanes) || + maySuspendCommitInSyncRender(type, props) + ) { + suspendInstance(instance, type, props); + } } } break; } case HostComponent: { - recursivelyAccumulateSuspenseyCommit(fiber); + recursivelyAccumulateSuspenseyCommit(fiber, committedLanes); if (fiber.flags & suspenseyCommitFlag) { + const instance = fiber.stateNode; const type = fiber.type; const props = fiber.memoizedProps; - suspendInstance(type, props); + // TODO: Allow sync lanes to suspend too with an opt-in. + if ( + includesOnlySuspenseyCommitEligibleLanes(committedLanes) || + maySuspendCommitInSyncRender(type, props) + ) { + suspendInstance(instance, type, props); + } } break; } @@ -4331,10 +4355,10 @@ function accumulateSuspenseyCommitOnFiber(fiber: Fiber) { const container: Container = fiber.stateNode.containerInfo; currentHoistableRoot = getHoistableRoot(container); - recursivelyAccumulateSuspenseyCommit(fiber); + recursivelyAccumulateSuspenseyCommit(fiber, committedLanes); currentHoistableRoot = previousHoistableRoot; } else { - recursivelyAccumulateSuspenseyCommit(fiber); + recursivelyAccumulateSuspenseyCommit(fiber, committedLanes); } break; } @@ -4352,10 +4376,10 @@ function accumulateSuspenseyCommitOnFiber(fiber: Fiber) { // instances, even if they're in the current tree. const prevFlags = suspenseyCommitFlag; suspenseyCommitFlag = MaySuspendCommit; - recursivelyAccumulateSuspenseyCommit(fiber); + recursivelyAccumulateSuspenseyCommit(fiber, committedLanes); suspenseyCommitFlag = prevFlags; } else { - recursivelyAccumulateSuspenseyCommit(fiber); + recursivelyAccumulateSuspenseyCommit(fiber, committedLanes); } } break; @@ -4375,13 +4399,13 @@ function accumulateSuspenseyCommitOnFiber(fiber: Fiber) { trackAppearingViewTransition(name, state); } } - recursivelyAccumulateSuspenseyCommit(fiber); + recursivelyAccumulateSuspenseyCommit(fiber, committedLanes); break; } // Fallthrough } default: { - recursivelyAccumulateSuspenseyCommit(fiber); + recursivelyAccumulateSuspenseyCommit(fiber, committedLanes); } } } diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.js b/packages/react-reconciler/src/ReactFiberCompleteWork.js index 663523dc952de..037c1d3bef42d 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.js @@ -116,6 +116,8 @@ import { preparePortalMount, prepareScopeUpdate, maySuspendCommit, + maySuspendCommitOnUpdate, + maySuspendCommitInSyncRender, mayResourceSuspendCommit, preloadInstance, preloadResource, @@ -167,6 +169,7 @@ import { includesSomeLane, mergeLanes, claimNextRetryLane, + includesOnlySuspenseyCommitEligibleLanes, } from './ReactFiberLane'; import {resetChildFibers} from './ReactChildFiber'; import {createScopeInstance} from './ReactFiberScope'; @@ -547,10 +550,16 @@ function updateHostComponent( function preloadInstanceAndSuspendIfNeeded( workInProgress: Fiber, type: Type, - props: Props, + oldProps: null | Props, + newProps: Props, renderLanes: Lanes, ) { - if (!maySuspendCommit(type, props)) { + const maySuspend = + oldProps === null + ? maySuspendCommit(type, newProps) + : maySuspendCommitOnUpdate(type, oldProps, newProps); + + if (!maySuspend) { // If this flag was set previously, we can remove it. The flag // represents whether this particular set of props might ever need to // suspend. The safest thing to do is for maySuspendCommit to always @@ -568,15 +577,25 @@ function preloadInstanceAndSuspendIfNeeded( // loaded yet. workInProgress.flags |= MaySuspendCommit; - // preload the instance if necessary. Even if this is an urgent render there - // could be benefits to preloading early. - // @TODO we should probably do the preload in begin work - const isReady = preloadInstance(type, props); - if (!isReady) { - if (shouldRemainOnPreviousScreen()) { - workInProgress.flags |= ShouldSuspendCommit; + if ( + includesOnlySuspenseyCommitEligibleLanes(renderLanes) || + maySuspendCommitInSyncRender(type, newProps) + ) { + // preload the instance if necessary. Even if this is an urgent render there + // could be benefits to preloading early. + // @TODO we should probably do the preload in begin work + const isReady = preloadInstance(workInProgress.stateNode, type, newProps); + if (!isReady) { + if (shouldRemainOnPreviousScreen()) { + workInProgress.flags |= ShouldSuspendCommit; + } else { + suspendCommit(); + } } else { - suspendCommit(); + // Even if we're ready we suspend the commit and check again in the pre-commit + // phase if we need to suspend anyway. Such as if it's delayed on decoding or + // if it was dropped from the cache while rendering due to pressure. + workInProgress.flags |= ShouldSuspendCommit; } } } @@ -1104,6 +1123,7 @@ function completeWork( preloadInstanceAndSuspendIfNeeded( workInProgress, type, + null, newProps, renderLanes, ); @@ -1137,10 +1157,10 @@ function completeWork( return null; } } else { + const oldProps = current.memoizedProps; // This is an Instance // We may have props to update on the Hoistable instance. if (supportsMutation) { - const oldProps = current.memoizedProps; if (oldProps !== newProps) { markUpdate(workInProgress); } @@ -1160,6 +1180,7 @@ function completeWork( preloadInstanceAndSuspendIfNeeded( workInProgress, type, + oldProps, newProps, renderLanes, ); @@ -1323,6 +1344,7 @@ function completeWork( preloadInstanceAndSuspendIfNeeded( workInProgress, workInProgress.type, + current === null ? null : current.memoizedProps, workInProgress.pendingProps, renderLanes, ); diff --git a/packages/react-reconciler/src/ReactFiberLane.js b/packages/react-reconciler/src/ReactFiberLane.js index eb65e69508cec..6548aeac3af38 100644 --- a/packages/react-reconciler/src/ReactFiberLane.js +++ b/packages/react-reconciler/src/ReactFiberLane.js @@ -637,6 +637,14 @@ export function includesOnlyViewTransitionEligibleLanes(lanes: Lanes): boolean { return (lanes & (TransitionLanes | RetryLanes | IdleLane)) === lanes; } +export function includesOnlySuspenseyCommitEligibleLanes( + lanes: Lanes, +): boolean { + return ( + (lanes & (TransitionLanes | RetryLanes | IdleLane | GestureLane)) === lanes + ); +} + export function includesBlockingLane(lanes: Lanes): boolean { const SyncDefaultLanes = InputContinuousHydrationLane | diff --git a/packages/react-reconciler/src/ReactFiberPerformanceTrack.js b/packages/react-reconciler/src/ReactFiberPerformanceTrack.js index 9a59997521871..042c3fd927eff 100644 --- a/packages/react-reconciler/src/ReactFiberPerformanceTrack.js +++ b/packages/react-reconciler/src/ReactFiberPerformanceTrack.js @@ -645,9 +645,9 @@ export function logSuspendedCommitPhase( reusableLaneDevToolDetails.color = 'secondary-light'; reusableLaneOptions.start = startTime; reusableLaneOptions.end = endTime; - // TODO: Make this conditionally "Suspended on Images" or both when we add Suspensey Images. + // TODO: Include the exact reason and URLs of what resources suspended. // TODO: This might also be Suspended while waiting on a View Transition. - performance.measure('Suspended on CSS', reusableLaneOptions); + performance.measure('Suspended on CSS or Images', reusableLaneOptions); } } diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index e7d04d9f94ab9..ca1c59e85b782 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -1467,7 +1467,7 @@ function commitRootWhenReady( // transaction, so it track state in its own module scope. // This will also track any newly added or appearing ViewTransition // components for the purposes of forming pairs. - accumulateSuspenseyCommit(finishedWork); + accumulateSuspenseyCommit(finishedWork, lanes); if (isViewTransitionEligible || isGestureTransition) { // If we're stopping gestures we don't have to wait for any pending // view transition. We'll stop it when we commit. @@ -2638,7 +2638,7 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) { const props = hostFiber.pendingProps; const isReady = resource ? preloadResource(resource) - : preloadInstance(type, props); + : preloadInstance(hostFiber.stateNode, type, props); if (isReady) { // The data resolved. Resume the work loop as if nothing // suspended. Unlike when a user component suspends, we don't diff --git a/packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js b/packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js index 7ceba3439f6ce..2351f7ed01b8f 100644 --- a/packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js @@ -97,11 +97,17 @@ describe('ReactFiberHostContext', () => { maySuspendCommit(type, props) { return false; }, - preloadInstance(type, props) { + maySuspendCommitOnUpdate(type, oldProps, newProps) { + return false; + }, + maySuspendCommitInSyncRender(type, props) { + return false; + }, + preloadInstance(instance, type, props) { return true; }, startSuspendingCommit() {}, - suspendInstance(type, props) {}, + suspendInstance(instance, type, props) {}, suspendOnActiveViewTransition(container) {}, waitForCommitToBeReady() { return null; diff --git a/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js b/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js index 050b1650c3b69..f890a78a80e71 100644 --- a/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js +++ b/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js @@ -88,6 +88,9 @@ export const shouldAttemptEagerTransition = export const detachDeletedInstance = $$$config.detachDeletedInstance; export const requestPostPaintCallback = $$$config.requestPostPaintCallback; export const maySuspendCommit = $$$config.maySuspendCommit; +export const maySuspendCommitOnUpdate = $$$config.maySuspendCommitOnUpdate; +export const maySuspendCommitInSyncRender = + $$$config.maySuspendCommitInSyncRender; export const preloadInstance = $$$config.preloadInstance; export const startSuspendingCommit = $$$config.startSuspendingCommit; export const suspendInstance = $$$config.suspendInstance; diff --git a/packages/react-test-renderer/src/ReactFiberConfigTestHost.js b/packages/react-test-renderer/src/ReactFiberConfigTestHost.js index c0ebf59601817..05fa9dffa3608 100644 --- a/packages/react-test-renderer/src/ReactFiberConfigTestHost.js +++ b/packages/react-test-renderer/src/ReactFiberConfigTestHost.js @@ -537,14 +537,37 @@ export function maySuspendCommit(type: Type, props: Props): boolean { return false; } -export function preloadInstance(type: Type, props: Props): boolean { +export function maySuspendCommitOnUpdate( + type: Type, + oldProps: Props, + newProps: Props, +): boolean { + return false; +} + +export function maySuspendCommitInSyncRender( + type: Type, + props: Props, +): boolean { + return false; +} + +export function preloadInstance( + instance: Instance, + type: Type, + props: Props, +): boolean { // Return true to indicate it's already loaded return true; } export function startSuspendingCommit(): void {} -export function suspendInstance(type: Type, props: Props): void {} +export function suspendInstance( + instance: Instance, + type: Type, + props: Props, +): void {} export function suspendOnActiveViewTransition(container: Container): void {} diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 106460b7c08fd..ebd15d1ae138a 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -96,6 +96,8 @@ export const enableGestureTransition = __EXPERIMENTAL__; export const enableScrollEndPolyfill = __EXPERIMENTAL__; +export const enableSuspenseyImages = __EXPERIMENTAL__; + /** * Switches the Fabric API from doing layout in commit work instead of complete work. */ diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 0f34a6364fa6e..25a8e1f5115d1 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -82,6 +82,7 @@ export const enableThrottledScheduling = false; export const enableViewTransition = false; export const enableGestureTransition = false; export const enableScrollEndPolyfill = true; +export const enableSuspenseyImages = false; export const enableFragmentRefs = false; export const ownerStackLimit = 1e4; diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 33d97eca70405..caf0f67c32b66 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -74,6 +74,7 @@ export const enableGestureTransition = false; export const enableFastAddPropertiesInDiffing = false; export const enableLazyPublicInstanceInFabric = false; export const enableScrollEndPolyfill = true; +export const enableSuspenseyImages = false; export const ownerStackLimit = 1e4; export const enableFragmentRefs = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 0162b75a7ae87..3b4916a22c8bd 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -73,6 +73,7 @@ export const enableGestureTransition = false; export const enableFastAddPropertiesInDiffing = true; export const enableLazyPublicInstanceInFabric = false; export const enableScrollEndPolyfill = true; +export const enableSuspenseyImages = false; export const ownerStackLimit = 1e4; export const enableFragmentRefs = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js index 3809542ddfd9c..33ff8a8acc211 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js @@ -70,6 +70,7 @@ export const enableGestureTransition = false; export const enableFastAddPropertiesInDiffing = false; export const enableLazyPublicInstanceInFabric = false; export const enableScrollEndPolyfill = true; +export const enableSuspenseyImages = false; export const enableFragmentRefs = false; export const ownerStackLimit = 1e4; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 422c6ce471693..cde11ce03c4d1 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -84,6 +84,7 @@ export const enableGestureTransition = false; export const enableFastAddPropertiesInDiffing = false; export const enableLazyPublicInstanceInFabric = false; export const enableScrollEndPolyfill = true; +export const enableSuspenseyImages = false; export const enableFragmentRefs = false; export const ownerStackLimit = 1e4; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 23ac44b37c6a6..62da5ecf77e00 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -113,6 +113,8 @@ export const enableLazyPublicInstanceInFabric = false; export const enableGestureTransition = false; +export const enableSuspenseyImages = false; + export const ownerStackLimit = 1e4; // Flow magic to verify the exports of this file match the original version.