Skip to content

Commit

Permalink
Offscreen: Use JS stack to track hidden/unhidden subtree state
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Vaughn committed Apr 7, 2021
1 parent ee6a05c commit 44174cd
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 70 deletions.
91 changes: 56 additions & 35 deletions packages/react-reconciler/src/ReactFiberCommitWork.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,14 +152,6 @@ if (__DEV__) {
didWarnAboutUndefinedSnapshotBeforeUpdate = new Set();
}

// Used during the commit phase to track the state of the Offscreen component stack.
// Allows us to avoid traversing the return path to find the nearest Offscreen ancestor.
// Only used when enableSuspenseLayoutEffectSemantics is enabled.
let offscreenSubtreeIsHidden: boolean = false;
const offscreenSubtreeIsHiddenStack: Array<boolean> = [];
let offscreenSubtreeWasHidden: boolean = false;
const offscreenSubtreeWasHiddenStack: Array<boolean> = [];

const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;

let nextEffect: Fiber | null = null;
Expand Down Expand Up @@ -2283,13 +2275,38 @@ export function commitLayoutEffects(
committedLanes: Lanes,
): void {
nextEffect = finishedWork;
commitLayoutEffects_begin(finishedWork, root, committedLanes);
commitLayoutEffects_intermediate(
finishedWork,
root,
committedLanes,
false,
false,
);
}

export function commitLayoutEffects_intermediate(
finishedWork: Fiber,
root: FiberRoot,
committedLanes: Lanes,
subtreeWasHidden: boolean,
subtreeIsHidden: boolean,
): void {
nextEffect = finishedWork;
commitLayoutEffects_begin(
finishedWork,
root,
committedLanes,
subtreeWasHidden,
subtreeIsHidden,
);
}

function commitLayoutEffects_begin(
subtreeRoot: Fiber,
root: FiberRoot,
committedLanes: Lanes,
subtreeWasHidden: boolean,
subtreeIsHidden: boolean,
) {
// Suspense layout effects semantics don't change for legacy roots.
const isModernRoot = (subtreeRoot.mode & ConcurrentMode) !== NoMode;
Expand All @@ -2305,11 +2322,25 @@ function commitLayoutEffects_begin(
const wasHidden = current !== null && current.memoizedState !== null;
const isHidden = fiber.memoizedState !== null;

offscreenSubtreeWasHidden = wasHidden || offscreenSubtreeWasHidden;
offscreenSubtreeIsHidden = isHidden || offscreenSubtreeIsHidden;
const newOffscreenSubtreeWasHidden = wasHidden || subtreeWasHidden;
const newOffscreenSubtreeIsHidden = isHidden || subtreeIsHidden;

offscreenSubtreeWasHiddenStack.push(wasHidden);
offscreenSubtreeIsHiddenStack.push(isHidden);
if (
newOffscreenSubtreeWasHidden !== subtreeWasHidden ||
newOffscreenSubtreeIsHidden !== subtreeIsHidden
) {
// Traverse the Offscreen subtree with the current Offscreen as the root.
nextEffect = fiber.child;
commitLayoutEffects_intermediate(
fiber, // New root; bubble back up to here and stop.
root,
committedLanes,
newOffscreenSubtreeWasHidden,
newOffscreenSubtreeIsHidden,
);
nextEffect = fiber.sibling;
continue;
}
}
}

Expand All @@ -2318,8 +2349,7 @@ function commitLayoutEffects_begin(
nextEffect = firstChild;
} else {
if (enableSuspenseLayoutEffectSemantics && isModernRoot) {
const visibilityChanged =
!offscreenSubtreeIsHidden && offscreenSubtreeWasHidden;
const visibilityChanged = !subtreeIsHidden && subtreeWasHidden;
if (
visibilityChanged &&
(fiber.subtreeFlags & LayoutStatic) !== NoFlags &&
Expand All @@ -2334,7 +2364,13 @@ function commitLayoutEffects_begin(
}
}

commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes);
commitLayoutMountEffects_complete(
subtreeRoot,
root,
committedLanes,
subtreeWasHidden,
subtreeIsHidden,
);
}
}
}
Expand All @@ -2343,35 +2379,20 @@ function commitLayoutMountEffects_complete(
subtreeRoot: Fiber,
root: FiberRoot,
committedLanes: Lanes,
subtreeWasHidden: boolean,
subtreeIsHidden: boolean,
) {
// Suspense layout effects semantics don't change for legacy roots.
const isModernRoot = (subtreeRoot.mode & ConcurrentMode) !== NoMode;

while (nextEffect !== null) {
const fiber = nextEffect;

if (enableSuspenseLayoutEffectSemantics && isModernRoot) {
if (fiber.tag === OffscreenComponent) {
offscreenSubtreeWasHiddenStack.pop();
offscreenSubtreeIsHiddenStack.pop();
offscreenSubtreeWasHidden =
offscreenSubtreeWasHiddenStack.length > 0 &&
offscreenSubtreeWasHiddenStack[
offscreenSubtreeWasHiddenStack.length - 1
];
offscreenSubtreeIsHidden =
offscreenSubtreeIsHiddenStack.length > 0 &&
offscreenSubtreeIsHiddenStack[
offscreenSubtreeIsHiddenStack.length - 1
];
}
}

if (
enableSuspenseLayoutEffectSemantics &&
isModernRoot &&
offscreenSubtreeWasHidden &&
!offscreenSubtreeIsHidden
subtreeWasHidden &&
!subtreeIsHidden
) {
// Inside of an Offscreen subtree that changed visibility during this commit.
// If this subtree was hidden, layout effects will have already been destroyed (during mutation phase)
Expand Down
91 changes: 56 additions & 35 deletions packages/react-reconciler/src/ReactFiberCommitWork.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,14 +152,6 @@ if (__DEV__) {
didWarnAboutUndefinedSnapshotBeforeUpdate = new Set();
}

// Used during the commit phase to track the state of the Offscreen component stack.
// Allows us to avoid traversing the return path to find the nearest Offscreen ancestor.
// Only used when enableSuspenseLayoutEffectSemantics is enabled.
let offscreenSubtreeIsHidden: boolean = false;
const offscreenSubtreeIsHiddenStack: Array<boolean> = [];
let offscreenSubtreeWasHidden: boolean = false;
const offscreenSubtreeWasHiddenStack: Array<boolean> = [];

const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;

let nextEffect: Fiber | null = null;
Expand Down Expand Up @@ -2283,13 +2275,38 @@ export function commitLayoutEffects(
committedLanes: Lanes,
): void {
nextEffect = finishedWork;
commitLayoutEffects_begin(finishedWork, root, committedLanes);
commitLayoutEffects_intermediate(
finishedWork,
root,
committedLanes,
false,
false,
);
}

export function commitLayoutEffects_intermediate(
finishedWork: Fiber,
root: FiberRoot,
committedLanes: Lanes,
subtreeWasHidden: boolean,
subtreeIsHidden: boolean,
): void {
nextEffect = finishedWork;
commitLayoutEffects_begin(
finishedWork,
root,
committedLanes,
subtreeWasHidden,
subtreeIsHidden,
);
}

function commitLayoutEffects_begin(
subtreeRoot: Fiber,
root: FiberRoot,
committedLanes: Lanes,
subtreeWasHidden: boolean,
subtreeIsHidden: boolean,
) {
// Suspense layout effects semantics don't change for legacy roots.
const isModernRoot = (subtreeRoot.mode & ConcurrentMode) !== NoMode;
Expand All @@ -2305,11 +2322,25 @@ function commitLayoutEffects_begin(
const wasHidden = current !== null && current.memoizedState !== null;
const isHidden = fiber.memoizedState !== null;

offscreenSubtreeWasHidden = wasHidden || offscreenSubtreeWasHidden;
offscreenSubtreeIsHidden = isHidden || offscreenSubtreeIsHidden;
const newOffscreenSubtreeWasHidden = wasHidden || subtreeWasHidden;
const newOffscreenSubtreeIsHidden = isHidden || subtreeIsHidden;

offscreenSubtreeWasHiddenStack.push(wasHidden);
offscreenSubtreeIsHiddenStack.push(isHidden);
if (
newOffscreenSubtreeWasHidden !== subtreeWasHidden ||
newOffscreenSubtreeIsHidden !== subtreeIsHidden
) {
// Traverse the Offscreen subtree with the current Offscreen as the root.
nextEffect = fiber.child;
commitLayoutEffects_intermediate(
fiber, // New root; bubble back up to here and stop.
root,
committedLanes,
newOffscreenSubtreeWasHidden,
newOffscreenSubtreeIsHidden,
);
nextEffect = fiber.sibling;
continue;
}
}
}

Expand All @@ -2318,8 +2349,7 @@ function commitLayoutEffects_begin(
nextEffect = firstChild;
} else {
if (enableSuspenseLayoutEffectSemantics && isModernRoot) {
const visibilityChanged =
!offscreenSubtreeIsHidden && offscreenSubtreeWasHidden;
const visibilityChanged = !subtreeIsHidden && subtreeWasHidden;
if (
visibilityChanged &&
(fiber.subtreeFlags & LayoutStatic) !== NoFlags &&
Expand All @@ -2334,7 +2364,13 @@ function commitLayoutEffects_begin(
}
}

commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes);
commitLayoutMountEffects_complete(
subtreeRoot,
root,
committedLanes,
subtreeWasHidden,
subtreeIsHidden,
);
}
}
}
Expand All @@ -2343,35 +2379,20 @@ function commitLayoutMountEffects_complete(
subtreeRoot: Fiber,
root: FiberRoot,
committedLanes: Lanes,
subtreeWasHidden: boolean,
subtreeIsHidden: boolean,
) {
// Suspense layout effects semantics don't change for legacy roots.
const isModernRoot = (subtreeRoot.mode & ConcurrentMode) !== NoMode;

while (nextEffect !== null) {
const fiber = nextEffect;

if (enableSuspenseLayoutEffectSemantics && isModernRoot) {
if (fiber.tag === OffscreenComponent) {
offscreenSubtreeWasHiddenStack.pop();
offscreenSubtreeIsHiddenStack.pop();
offscreenSubtreeWasHidden =
offscreenSubtreeWasHiddenStack.length > 0 &&
offscreenSubtreeWasHiddenStack[
offscreenSubtreeWasHiddenStack.length - 1
];
offscreenSubtreeIsHidden =
offscreenSubtreeIsHiddenStack.length > 0 &&
offscreenSubtreeIsHiddenStack[
offscreenSubtreeIsHiddenStack.length - 1
];
}
}

if (
enableSuspenseLayoutEffectSemantics &&
isModernRoot &&
offscreenSubtreeWasHidden &&
!offscreenSubtreeIsHidden
subtreeWasHidden &&
!subtreeIsHidden
) {
// Inside of an Offscreen subtree that changed visibility during this commit.
// If this subtree was hidden, layout effects will have already been destroyed (during mutation phase)
Expand Down

0 comments on commit 44174cd

Please sign in to comment.