Skip to content

Commit

Permalink
[Fabric] Fix reparenting in legacy Suspense mount
Browse files Browse the repository at this point in the history
Fixes a weird case during legacy Suspense mount where the offscreen host
container of a tree that suspends during initial mount is recreated
instead of cloned, since there's no current fiber to clone from.

Fabric considers this a reparent even though the parent from the first
pass never committed.

Instead we can override the props from the first pass before the
container completes. It's a bit of a hack, but no more so than the rest
of the legacy root Suspense implementation — the hacks are designed
to make it usable by non-strict mode-compliant trees.
  • Loading branch information
acdlite committed Jul 30, 2021
1 parent 7addf44 commit b76acc3
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 30 deletions.
15 changes: 0 additions & 15 deletions packages/react-reconciler/src/ReactFiberBeginWork.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -2212,21 +2212,6 @@ function mountSuspenseFallbackChildren(
primaryChildFragment.childLanes = NoLanes;
primaryChildFragment.pendingProps = primaryChildProps;

if (
supportsPersistence &&
(workInProgress.mode & ConcurrentMode) === NoMode
) {
const isHidden = true;
const offscreenContainer: Fiber = (primaryChildFragment.child: any);
const containerProps = {
hidden: isHidden,
primaryChildren,
};
offscreenContainer.pendingProps = containerProps;
offscreenContainer.memoizedProps = containerProps;
completeSuspendedOffscreenHostContainer(null, offscreenContainer);
}

if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
// Reset the durations from the first pass so they aren't included in the
// final amounts. This seems counterintuitive, since we're intentionally
Expand Down
15 changes: 0 additions & 15 deletions packages/react-reconciler/src/ReactFiberBeginWork.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -2212,21 +2212,6 @@ function mountSuspenseFallbackChildren(
primaryChildFragment.childLanes = NoLanes;
primaryChildFragment.pendingProps = primaryChildProps;

if (
supportsPersistence &&
(workInProgress.mode & ConcurrentMode) === NoMode
) {
const isHidden = true;
const offscreenContainer: Fiber = (primaryChildFragment.child: any);
const containerProps = {
hidden: isHidden,
primaryChildren,
};
offscreenContainer.pendingProps = containerProps;
offscreenContainer.memoizedProps = containerProps;
completeSuspendedOffscreenHostContainer(null, offscreenContainer);
}

if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
// Reset the durations from the first pass so they aren't included in the
// final amounts. This seems counterintuitive, since we're intentionally
Expand Down
24 changes: 24 additions & 0 deletions packages/react-reconciler/src/ReactFiberThrow.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ import {
LifecycleEffectMask,
ForceUpdateForLegacySuspense,
} from './ReactFiberFlags';
import {
supportsPersistence,
getOffscreenContainerProps,
} from './ReactFiberHostConfig';
import {shouldCaptureSuspense} from './ReactFiberSuspenseComponent.new';
import {NoMode, ConcurrentMode, DebugTracingMode} from './ReactTypeOfMode';
import {
Expand Down Expand Up @@ -313,6 +317,26 @@ function throwException(
// all lifecycle effect tags.
sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete);

if (supportsPersistence) {
// Another legacy Suspense quirk. In persistent mode, if this is the
// initial mount, override the props of the host container to hide
// its contents.
const currentSuspenseBoundary = workInProgress.alternate;
if (currentSuspenseBoundary === null) {
const offscreenFiber: Fiber = (workInProgress.child: any);
const offscreenContainer = offscreenFiber.child;
if (offscreenContainer !== null) {
const children = offscreenContainer.memoizedProps.children;
const containerProps = getOffscreenContainerProps(
'hidden',
children,
);
offscreenContainer.pendingProps = containerProps;
offscreenContainer.memoizedProps = containerProps;
}
}
}

if (sourceFiber.tag === ClassComponent) {
const currentSourceFiber = sourceFiber.alternate;
if (currentSourceFiber === null) {
Expand Down
24 changes: 24 additions & 0 deletions packages/react-reconciler/src/ReactFiberThrow.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ import {
LifecycleEffectMask,
ForceUpdateForLegacySuspense,
} from './ReactFiberFlags';
import {
supportsPersistence,
getOffscreenContainerProps,
} from './ReactFiberHostConfig';
import {shouldCaptureSuspense} from './ReactFiberSuspenseComponent.old';
import {NoMode, ConcurrentMode, DebugTracingMode} from './ReactTypeOfMode';
import {
Expand Down Expand Up @@ -313,6 +317,26 @@ function throwException(
// all lifecycle effect tags.
sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete);

if (supportsPersistence) {
// Another legacy Suspense quirk. In persistent mode, if this is the
// initial mount, override the props of the host container to hide
// its contents.
const currentSuspenseBoundary = workInProgress.alternate;
if (currentSuspenseBoundary === null) {
const offscreenFiber: Fiber = (workInProgress.child: any);
const offscreenContainer = offscreenFiber.child;
if (offscreenContainer !== null) {
const children = offscreenContainer.memoizedProps.children;
const containerProps = getOffscreenContainerProps(
'hidden',
children,
);
offscreenContainer.pendingProps = containerProps;
offscreenContainer.memoizedProps = containerProps;
}
}
}

if (sourceFiber.tag === ClassComponent) {
const currentSourceFiber = sourceFiber.alternate;
if (currentSourceFiber === null) {
Expand Down

0 comments on commit b76acc3

Please sign in to comment.