Skip to content

Commit bdd3d08

Browse files
committed
Extract logic for detecting bad fallback to helper
Pure refactor, no change in behavior. Extracts the logic for detecting whether a suspended component will result in a "bad" Suspense fallback into a helper function. An example of a bad Suspense fallback is one that causes already-visible content to disappear. I want to reuse this same logic in the work loop, too.
1 parent 952dfff commit bdd3d08

6 files changed

+70
-40
lines changed

packages/react-reconciler/src/ReactFiberCompleteWork.new.js

+3-19
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import type {OffscreenState} from './ReactFiberOffscreenComponent';
3131
import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent.new';
3232
import type {Cache} from './ReactFiberCacheComponent.new';
3333
import {
34-
enableSuspenseAvoidThisFallback,
3534
enableLegacyHidden,
3635
enableHostSingletons,
3736
enableSuspenseCallback,
@@ -127,11 +126,9 @@ import {
127126
setShallowSuspenseListContext,
128127
ForceSuspenseFallback,
129128
setDefaultShallowSuspenseListContext,
129+
isBadSuspenseFallback,
130130
} from './ReactFiberSuspenseContext.new';
131-
import {
132-
popHiddenContext,
133-
isCurrentTreeHidden,
134-
} from './ReactFiberHiddenContext.new';
131+
import {popHiddenContext} from './ReactFiberHiddenContext.new';
135132
import {findFirstSuspended} from './ReactFiberSuspenseComponent.new';
136133
import {
137134
isContextProvider as isLegacyContextProvider,
@@ -1272,20 +1269,7 @@ function completeWork(
12721269
// If this render already had a ping or lower pri updates,
12731270
// and this is the first time we know we're going to suspend we
12741271
// should be able to immediately restart from within throwException.
1275-
1276-
// Check if this is a "bad" fallback state or a good one. A bad
1277-
// fallback state is one that we only show as a last resort; if this
1278-
// is a transition, we'll block it from displaying, and wait for
1279-
// more data to arrive.
1280-
const isBadFallback =
1281-
// It's bad to switch to a fallback if content is already visible
1282-
(current !== null && !prevDidTimeout && !isCurrentTreeHidden()) ||
1283-
// Experimental: Some fallbacks are always bad
1284-
(enableSuspenseAvoidThisFallback &&
1285-
workInProgress.memoizedProps.unstable_avoidThisFallback ===
1286-
true);
1287-
1288-
if (isBadFallback) {
1272+
if (isBadSuspenseFallback(current, newProps)) {
12891273
renderDidSuspendDelayIfPossible();
12901274
} else {
12911275
renderDidSuspend();

packages/react-reconciler/src/ReactFiberCompleteWork.old.js

+3-19
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import type {OffscreenState} from './ReactFiberOffscreenComponent';
3131
import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent.old';
3232
import type {Cache} from './ReactFiberCacheComponent.old';
3333
import {
34-
enableSuspenseAvoidThisFallback,
3534
enableLegacyHidden,
3635
enableHostSingletons,
3736
enableSuspenseCallback,
@@ -127,11 +126,9 @@ import {
127126
setShallowSuspenseListContext,
128127
ForceSuspenseFallback,
129128
setDefaultShallowSuspenseListContext,
129+
isBadSuspenseFallback,
130130
} from './ReactFiberSuspenseContext.old';
131-
import {
132-
popHiddenContext,
133-
isCurrentTreeHidden,
134-
} from './ReactFiberHiddenContext.old';
131+
import {popHiddenContext} from './ReactFiberHiddenContext.old';
135132
import {findFirstSuspended} from './ReactFiberSuspenseComponent.old';
136133
import {
137134
isContextProvider as isLegacyContextProvider,
@@ -1272,20 +1269,7 @@ function completeWork(
12721269
// If this render already had a ping or lower pri updates,
12731270
// and this is the first time we know we're going to suspend we
12741271
// should be able to immediately restart from within throwException.
1275-
1276-
// Check if this is a "bad" fallback state or a good one. A bad
1277-
// fallback state is one that we only show as a last resort; if this
1278-
// is a transition, we'll block it from displaying, and wait for
1279-
// more data to arrive.
1280-
const isBadFallback =
1281-
// It's bad to switch to a fallback if content is already visible
1282-
(current !== null && !prevDidTimeout && !isCurrentTreeHidden()) ||
1283-
// Experimental: Some fallbacks are always bad
1284-
(enableSuspenseAvoidThisFallback &&
1285-
workInProgress.memoizedProps.unstable_avoidThisFallback ===
1286-
true);
1287-
1288-
if (isBadFallback) {
1272+
if (isBadSuspenseFallback(current, newProps)) {
12891273
renderDidSuspendDelayIfPossible();
12901274
} else {
12911275
renderDidSuspend();

packages/react-reconciler/src/ReactFiberSuspenseComponent.new.js

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export type SuspenseProps = {
2727
// TODO: Add "unstable_" prefix?
2828
suspenseCallback?: (Set<Wakeable> | null) => mixed,
2929

30+
unstable_avoidThisFallback?: boolean,
3031
unstable_expectedLoadTime?: number,
3132
unstable_name?: string,
3233
};

packages/react-reconciler/src/ReactFiberSuspenseComponent.old.js

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export type SuspenseProps = {
2727
// TODO: Add "unstable_" prefix?
2828
suspenseCallback?: (Set<Wakeable> | null) => mixed,
2929

30+
unstable_avoidThisFallback?: boolean,
3031
unstable_expectedLoadTime?: number,
3132
unstable_name?: string,
3233
};

packages/react-reconciler/src/ReactFiberSuspenseContext.new.js

+31-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99

1010
import type {Fiber} from './ReactInternalTypes';
1111
import type {StackCursor} from './ReactFiberStack.new';
12-
import type {SuspenseState} from './ReactFiberSuspenseComponent.new';
12+
import type {
13+
SuspenseState,
14+
SuspenseProps,
15+
} from './ReactFiberSuspenseComponent.new';
1316

1417
import {enableSuspenseAvoidThisFallback} from 'shared/ReactFeatureFlags';
1518
import {createCursor, push, pop} from './ReactFiberStack.new';
@@ -55,6 +58,33 @@ function shouldAvoidedBoundaryCapture(
5558
return false;
5659
}
5760

61+
export function isBadSuspenseFallback(
62+
current: Fiber | null,
63+
nextProps: SuspenseProps,
64+
): boolean {
65+
// Check if this is a "bad" fallback state or a good one. A bad fallback state
66+
// is one that we only show as a last resort; if this is a transition, we'll
67+
// block it from displaying, and wait for more data to arrive.
68+
if (current !== null) {
69+
const prevState: SuspenseState = current.memoizedState;
70+
const isShowingFallback = prevState !== null;
71+
if (!isShowingFallback && !isCurrentTreeHidden()) {
72+
// It's bad to switch to a fallback if content is already visible
73+
return true;
74+
}
75+
}
76+
77+
if (
78+
enableSuspenseAvoidThisFallback &&
79+
nextProps.unstable_avoidThisFallback === true
80+
) {
81+
// Experimental: Some fallbacks are always bad
82+
return true;
83+
}
84+
85+
return false;
86+
}
87+
5888
export function pushPrimaryTreeSuspenseHandler(handler: Fiber): void {
5989
const props = handler.pendingProps;
6090
const handlerOnStack = suspenseHandlerStackCursor.current;

packages/react-reconciler/src/ReactFiberSuspenseContext.old.js

+31-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99

1010
import type {Fiber} from './ReactInternalTypes';
1111
import type {StackCursor} from './ReactFiberStack.old';
12-
import type {SuspenseState} from './ReactFiberSuspenseComponent.old';
12+
import type {
13+
SuspenseState,
14+
SuspenseProps,
15+
} from './ReactFiberSuspenseComponent.old';
1316

1417
import {enableSuspenseAvoidThisFallback} from 'shared/ReactFeatureFlags';
1518
import {createCursor, push, pop} from './ReactFiberStack.old';
@@ -55,6 +58,33 @@ function shouldAvoidedBoundaryCapture(
5558
return false;
5659
}
5760

61+
export function isBadSuspenseFallback(
62+
current: Fiber | null,
63+
nextProps: SuspenseProps,
64+
): boolean {
65+
// Check if this is a "bad" fallback state or a good one. A bad fallback state
66+
// is one that we only show as a last resort; if this is a transition, we'll
67+
// block it from displaying, and wait for more data to arrive.
68+
if (current !== null) {
69+
const prevState: SuspenseState = current.memoizedState;
70+
const isShowingFallback = prevState !== null;
71+
if (!isShowingFallback && !isCurrentTreeHidden()) {
72+
// It's bad to switch to a fallback if content is already visible
73+
return true;
74+
}
75+
}
76+
77+
if (
78+
enableSuspenseAvoidThisFallback &&
79+
nextProps.unstable_avoidThisFallback === true
80+
) {
81+
// Experimental: Some fallbacks are always bad
82+
return true;
83+
}
84+
85+
return false;
86+
}
87+
5888
export function pushPrimaryTreeSuspenseHandler(handler: Fiber): void {
5989
const props = handler.pendingProps;
6090
const handlerOnStack = suspenseHandlerStackCursor.current;

0 commit comments

Comments
 (0)