Skip to content

Commit

Permalink
Extract logic for detecting bad fallback to helper
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
acdlite committed Oct 29, 2022
1 parent c3caa8f commit d3705ee
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 40 deletions.
22 changes: 3 additions & 19 deletions packages/react-reconciler/src/ReactFiberCompleteWork.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import type {OffscreenState} from './ReactFiberOffscreenComponent';
import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent.new';
import type {Cache} from './ReactFiberCacheComponent.new';
import {
enableSuspenseAvoidThisFallback,
enableLegacyHidden,
enableHostSingletons,
enableSuspenseCallback,
Expand Down Expand Up @@ -127,11 +126,9 @@ import {
setShallowSuspenseListContext,
ForceSuspenseFallback,
setDefaultShallowSuspenseListContext,
isBadSuspenseFallback,
} from './ReactFiberSuspenseContext.new';
import {
popHiddenContext,
isCurrentTreeHidden,
} from './ReactFiberHiddenContext.new';
import {popHiddenContext} from './ReactFiberHiddenContext.new';
import {findFirstSuspended} from './ReactFiberSuspenseComponent.new';
import {
isContextProvider as isLegacyContextProvider,
Expand Down Expand Up @@ -1272,20 +1269,7 @@ function completeWork(
// If this render already had a ping or lower pri updates,
// and this is the first time we know we're going to suspend we
// should be able to immediately restart from within throwException.

// Check if this is a "bad" fallback state or a good one. A bad
// fallback state is one that we only show as a last resort; if this
// is a transition, we'll block it from displaying, and wait for
// more data to arrive.
const isBadFallback =
// It's bad to switch to a fallback if content is already visible
(current !== null && !prevDidTimeout && !isCurrentTreeHidden()) ||
// Experimental: Some fallbacks are always bad
(enableSuspenseAvoidThisFallback &&
workInProgress.memoizedProps.unstable_avoidThisFallback ===
true);

if (isBadFallback) {
if (isBadSuspenseFallback(current, newProps)) {
renderDidSuspendDelayIfPossible();
} else {
renderDidSuspend();
Expand Down
22 changes: 3 additions & 19 deletions packages/react-reconciler/src/ReactFiberCompleteWork.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import type {OffscreenState} from './ReactFiberOffscreenComponent';
import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent.old';
import type {Cache} from './ReactFiberCacheComponent.old';
import {
enableSuspenseAvoidThisFallback,
enableLegacyHidden,
enableHostSingletons,
enableSuspenseCallback,
Expand Down Expand Up @@ -127,11 +126,9 @@ import {
setShallowSuspenseListContext,
ForceSuspenseFallback,
setDefaultShallowSuspenseListContext,
isBadSuspenseFallback,
} from './ReactFiberSuspenseContext.old';
import {
popHiddenContext,
isCurrentTreeHidden,
} from './ReactFiberHiddenContext.old';
import {popHiddenContext} from './ReactFiberHiddenContext.old';
import {findFirstSuspended} from './ReactFiberSuspenseComponent.old';
import {
isContextProvider as isLegacyContextProvider,
Expand Down Expand Up @@ -1272,20 +1269,7 @@ function completeWork(
// If this render already had a ping or lower pri updates,
// and this is the first time we know we're going to suspend we
// should be able to immediately restart from within throwException.

// Check if this is a "bad" fallback state or a good one. A bad
// fallback state is one that we only show as a last resort; if this
// is a transition, we'll block it from displaying, and wait for
// more data to arrive.
const isBadFallback =
// It's bad to switch to a fallback if content is already visible
(current !== null && !prevDidTimeout && !isCurrentTreeHidden()) ||
// Experimental: Some fallbacks are always bad
(enableSuspenseAvoidThisFallback &&
workInProgress.memoizedProps.unstable_avoidThisFallback ===
true);

if (isBadFallback) {
if (isBadSuspenseFallback(current, newProps)) {
renderDidSuspendDelayIfPossible();
} else {
renderDidSuspend();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export type SuspenseProps = {
// TODO: Add "unstable_" prefix?
suspenseCallback?: (Set<Wakeable> | null) => mixed,

unstable_avoidThisFallback?: boolean,
unstable_expectedLoadTime?: number,
unstable_name?: string,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export type SuspenseProps = {
// TODO: Add "unstable_" prefix?
suspenseCallback?: (Set<Wakeable> | null) => mixed,

unstable_avoidThisFallback?: boolean,
unstable_expectedLoadTime?: number,
unstable_name?: string,
};
Expand Down
32 changes: 31 additions & 1 deletion packages/react-reconciler/src/ReactFiberSuspenseContext.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@

import type {Fiber} from './ReactInternalTypes';
import type {StackCursor} from './ReactFiberStack.new';
import type {SuspenseState} from './ReactFiberSuspenseComponent.new';
import type {
SuspenseState,
SuspenseProps,
} from './ReactFiberSuspenseComponent.new';

import {enableSuspenseAvoidThisFallback} from 'shared/ReactFeatureFlags';
import {createCursor, push, pop} from './ReactFiberStack.new';
Expand Down Expand Up @@ -55,6 +58,33 @@ function shouldAvoidedBoundaryCapture(
return false;
}

export function isBadSuspenseFallback(
current: Fiber | null,
nextProps: SuspenseProps,
): boolean {
// Check if this is a "bad" fallback state or a good one. A bad fallback state
// is one that we only show as a last resort; if this is a transition, we'll
// block it from displaying, and wait for more data to arrive.
if (current !== null) {
const prevState: SuspenseState = current.memoizedState;
const isShowingFallback = prevState !== null;
if (!isShowingFallback && !isCurrentTreeHidden()) {
// It's bad to switch to a fallback if content is already visible
return true;
}
}

if (
enableSuspenseAvoidThisFallback &&
nextProps.unstable_avoidThisFallback === true
) {
// Experimental: Some fallbacks are always bad
return true;
}

return false;
}

export function pushPrimaryTreeSuspenseHandler(handler: Fiber): void {
const props = handler.pendingProps;
const handlerOnStack = suspenseHandlerStackCursor.current;
Expand Down
32 changes: 31 additions & 1 deletion packages/react-reconciler/src/ReactFiberSuspenseContext.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@

import type {Fiber} from './ReactInternalTypes';
import type {StackCursor} from './ReactFiberStack.old';
import type {SuspenseState} from './ReactFiberSuspenseComponent.old';
import type {
SuspenseState,
SuspenseProps,
} from './ReactFiberSuspenseComponent.old';

import {enableSuspenseAvoidThisFallback} from 'shared/ReactFeatureFlags';
import {createCursor, push, pop} from './ReactFiberStack.old';
Expand Down Expand Up @@ -55,6 +58,33 @@ function shouldAvoidedBoundaryCapture(
return false;
}

export function isBadSuspenseFallback(
current: Fiber | null,
nextProps: SuspenseProps,
): boolean {
// Check if this is a "bad" fallback state or a good one. A bad fallback state
// is one that we only show as a last resort; if this is a transition, we'll
// block it from displaying, and wait for more data to arrive.
if (current !== null) {
const prevState: SuspenseState = current.memoizedState;
const isShowingFallback = prevState !== null;
if (!isShowingFallback && !isCurrentTreeHidden()) {
// It's bad to switch to a fallback if content is already visible
return true;
}
}

if (
enableSuspenseAvoidThisFallback &&
nextProps.unstable_avoidThisFallback === true
) {
// Experimental: Some fallbacks are always bad
return true;
}

return false;
}

export function pushPrimaryTreeSuspenseHandler(handler: Fiber): void {
const props = handler.pendingProps;
const handlerOnStack = suspenseHandlerStackCursor.current;
Expand Down

0 comments on commit d3705ee

Please sign in to comment.