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.