Skip to content
Prev Previous commit
Avoid triggering Suspense fallback if the DOM node indicates it is co…
…mplete

We will still visit this in the commit phase to decode() it in case it's not
decoded already.
  • Loading branch information
sebmarkbage committed Apr 4, 2025
commit 21eed10cdde48aa79cba4403ce83110de90cdd66
15 changes: 9 additions & 6 deletions packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -5019,14 +5019,17 @@ export function mayResourceSuspendCommit(resource: Resource): boolean {
);
}

export function preloadInstance(type: Type, props: Props): boolean {
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.
// We indicate that all images are not yet loaded and if they're able
// to hit cache we let the decode() do that. Even if we did maintain
// our own cache to know this, it's not a guarantee that the browser
// keeps it in decoded memory.
return false;
// 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 {
Expand Down
6 changes: 5 additions & 1 deletion packages/react-native-renderer/src/ReactFiberConfigFabric.js
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,11 @@ export function maySuspendCommitInSyncRender(
return false;
}

export function preloadInstance(type: Type, props: Props): boolean {
export function preloadInstance(
instance: Instance,
type: Type,
props: Props,
): boolean {
return true;
}

Expand Down
6 changes: 5 additions & 1 deletion packages/react-native-renderer/src/ReactFiberConfigNative.js
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,11 @@ export function maySuspendCommitInSyncRender(
return false;
}

export function preloadInstance(type: Type, props: Props): boolean {
export function preloadInstance(
instance: Instance,
type: Type,
props: Props,
): boolean {
// Return false to indicate it's already loaded
return true;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/react-noop-renderer/src/createReactNoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
);
},

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);
}
Expand Down
7 changes: 6 additions & 1 deletion packages/react-reconciler/src/ReactFiberCompleteWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -584,13 +584,18 @@ function preloadInstanceAndSuspendIfNeeded(
// 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, newProps);
const isReady = preloadInstance(workInProgress.stateNode, type, newProps);
if (!isReady) {
if (shouldRemainOnPreviousScreen()) {
workInProgress.flags |= ShouldSuspendCommit;
} else {
suspendCommit();
}
} else {
// 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;
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/react-reconciler/src/ReactFiberWorkLoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ describe('ReactFiberHostContext', () => {
maySuspendCommitInSyncRender(type, props) {
return false;
},
preloadInstance(type, props) {
preloadInstance(instance, type, props) {
return true;
},
startSuspendingCommit() {},
Expand Down
6 changes: 5 additions & 1 deletion packages/react-test-renderer/src/ReactFiberConfigTestHost.js
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,11 @@ export function maySuspendCommitInSyncRender(
return false;
}

export function preloadInstance(type: Type, props: Props): boolean {
export function preloadInstance(
instance: Instance,
type: Type,
props: Props,
): boolean {
// Return true to indicate it's already loaded
return true;
}
Expand Down