mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Refactor "disappear" logic into its own traversal (#21901)
Similar to #21898, but for "disappear" logic. Previously this lived inside `hideOrUnhideAllChildren`, the function that mutates the nearest DOM nodes to override their `display` style. This makes the feature work in persistent mode (Fabric); it didn't before because `hideOrUnhideAllChildren` only runs in mutation mode.
This commit is contained in:
@@ -980,12 +980,6 @@ function reappearLayoutEffectsOnFiber(node: Fiber) {
|
||||
}
|
||||
|
||||
function hideOrUnhideAllChildren(finishedWork, isHidden) {
|
||||
// Suspense layout effects semantics don't change for legacy roots.
|
||||
const isModernRoot = (finishedWork.mode & ConcurrentMode) !== NoMode;
|
||||
|
||||
const current = finishedWork.alternate;
|
||||
const wasHidden = current !== null && current.memoizedState !== null;
|
||||
|
||||
// Only hide or unhide the top-most host nodes.
|
||||
let hostSubtreeRoot = null;
|
||||
|
||||
@@ -1005,22 +999,6 @@ function hideOrUnhideAllChildren(finishedWork, isHidden) {
|
||||
unhideInstance(node.stateNode, node.memoizedProps);
|
||||
}
|
||||
}
|
||||
|
||||
if (enableSuspenseLayoutEffectSemantics && isModernRoot) {
|
||||
// This method is called during mutation; it should detach refs within a hidden subtree.
|
||||
// Attaching refs should be done elsewhere though (during layout).
|
||||
// TODO (Offscreen) Also check: flags & RefStatic
|
||||
if (isHidden) {
|
||||
safelyDetachRef(node, finishedWork);
|
||||
}
|
||||
|
||||
// TODO (Offscreen) Also check: subtreeFlags & (RefStatic | LayoutStatic)
|
||||
if (node.child !== null) {
|
||||
node.child.return = node;
|
||||
node = node.child;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (node.tag === HostText) {
|
||||
if (hostSubtreeRoot === null) {
|
||||
const instance = node.stateNode;
|
||||
@@ -1038,52 +1016,6 @@ function hideOrUnhideAllChildren(finishedWork, isHidden) {
|
||||
) {
|
||||
// Found a nested Offscreen component that is hidden.
|
||||
// Don't search any deeper. This tree should remain hidden.
|
||||
} else if (enableSuspenseLayoutEffectSemantics && isModernRoot) {
|
||||
// When a mounted Suspense subtree gets hidden again, destroy any nested layout effects.
|
||||
// TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
|
||||
switch (node.tag) {
|
||||
case FunctionComponent:
|
||||
case ForwardRef:
|
||||
case MemoComponent:
|
||||
case SimpleMemoComponent: {
|
||||
// Note that refs are attached by the useImperativeHandle() hook, not by commitAttachRef()
|
||||
if (isHidden && !wasHidden) {
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
node.mode & ProfileMode
|
||||
) {
|
||||
try {
|
||||
startLayoutEffectTimer();
|
||||
commitHookEffectListUnmount(HookLayout, node, finishedWork);
|
||||
} finally {
|
||||
recordLayoutEffectDuration(node);
|
||||
}
|
||||
} else {
|
||||
commitHookEffectListUnmount(HookLayout, node, finishedWork);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ClassComponent: {
|
||||
if (isHidden && !wasHidden) {
|
||||
// TODO (Offscreen) Check: flags & RefStatic
|
||||
safelyDetachRef(node, finishedWork);
|
||||
|
||||
const instance = node.stateNode;
|
||||
if (typeof instance.componentWillUnmount === 'function') {
|
||||
safelyCallComponentWillUnmount(node, finishedWork, instance);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (node.child !== null) {
|
||||
node.child.return = node;
|
||||
node = node.child;
|
||||
continue;
|
||||
}
|
||||
} else if (node.child !== null) {
|
||||
node.child.return = node;
|
||||
node = node.child;
|
||||
@@ -1801,6 +1733,11 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
|
||||
// This prevents sibling component effects from interfering with each other,
|
||||
// e.g. a destroy function in one component should never override a ref set
|
||||
// by a create function in another component during the same commit.
|
||||
// TODO: Check if we're inside an Offscreen subtree that disappeared
|
||||
// during this commit. If so, we would have already unmounted its
|
||||
// layout hooks. (However, since we null out the `destroy` function
|
||||
// right before calling it, the behavior is already correct, so this
|
||||
// would mostly be for modeling purposes.)
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
@@ -2183,20 +2120,36 @@ function commitMutationEffectsOnFiber(finishedWork: Fiber, root: FiberRoot) {
|
||||
switch (finishedWork.tag) {
|
||||
case SuspenseComponent: {
|
||||
const newState: OffscreenState | null = finishedWork.memoizedState;
|
||||
if (newState !== null) {
|
||||
markCommitTimeOfFallback();
|
||||
// Hide the Offscreen component that contains the primary children.
|
||||
// TODO: Ideally, this effect would have been scheduled on the
|
||||
// Offscreen fiber itself. That's how unhiding works: the Offscreen
|
||||
// component schedules an effect on itself. However, in this case, the
|
||||
// component didn't complete, so the fiber was never added to the
|
||||
// effect list in the normal path. We could have appended it to the
|
||||
// effect list in the Suspense component's second pass, but doing it
|
||||
// this way is less complicated. This would be simpler if we got rid
|
||||
// of the effect list and traversed the tree, like we're planning to
|
||||
// do.
|
||||
const primaryChildParent: Fiber = (finishedWork.child: any);
|
||||
hideOrUnhideAllChildren(primaryChildParent, true);
|
||||
const isHidden = newState !== null;
|
||||
const current = finishedWork.alternate;
|
||||
const wasHidden = current !== null && current.memoizedState !== null;
|
||||
const offscreenBoundary: Fiber = (finishedWork.child: any);
|
||||
|
||||
if (isHidden) {
|
||||
if (!wasHidden) {
|
||||
markCommitTimeOfFallback();
|
||||
if (supportsMutation) {
|
||||
hideOrUnhideAllChildren(offscreenBoundary, true);
|
||||
}
|
||||
if (
|
||||
enableSuspenseLayoutEffectSemantics &&
|
||||
(offscreenBoundary.mode & ConcurrentMode) !== NoMode
|
||||
) {
|
||||
let offscreenChild = offscreenBoundary.child;
|
||||
while (offscreenChild !== null) {
|
||||
nextEffect = offscreenChild;
|
||||
disappearLayoutEffects_begin(offscreenChild);
|
||||
offscreenChild = offscreenChild.sibling;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (wasHidden) {
|
||||
if (supportsMutation) {
|
||||
hideOrUnhideAllChildren(offscreenBoundary, false);
|
||||
}
|
||||
// TODO: Move re-appear call here for symmetry?
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2204,7 +2157,36 @@ function commitMutationEffectsOnFiber(finishedWork: Fiber, root: FiberRoot) {
|
||||
case LegacyHiddenComponent: {
|
||||
const newState: OffscreenState | null = finishedWork.memoizedState;
|
||||
const isHidden = newState !== null;
|
||||
hideOrUnhideAllChildren(finishedWork, isHidden);
|
||||
const current = finishedWork.alternate;
|
||||
const wasHidden = current !== null && current.memoizedState !== null;
|
||||
const offscreenBoundary: Fiber = finishedWork;
|
||||
|
||||
if (supportsMutation) {
|
||||
// TODO: This needs to run whenever there's an insertion or update
|
||||
// inside a hidden Offscreen tree.
|
||||
hideOrUnhideAllChildren(offscreenBoundary, isHidden);
|
||||
}
|
||||
|
||||
if (isHidden) {
|
||||
if (!wasHidden) {
|
||||
if (
|
||||
enableSuspenseLayoutEffectSemantics &&
|
||||
(offscreenBoundary.mode & ConcurrentMode) !== NoMode
|
||||
) {
|
||||
nextEffect = offscreenBoundary;
|
||||
let offscreenChild = offscreenBoundary.child;
|
||||
while (offscreenChild !== null) {
|
||||
nextEffect = offscreenChild;
|
||||
disappearLayoutEffects_begin(offscreenChild);
|
||||
offscreenChild = offscreenChild.sibling;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (wasHidden) {
|
||||
// TODO: Move re-appear call here for symmetry?
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2381,6 +2363,90 @@ function commitLayoutMountEffects_complete(
|
||||
}
|
||||
}
|
||||
|
||||
function disappearLayoutEffects_begin(subtreeRoot: Fiber) {
|
||||
while (nextEffect !== null) {
|
||||
const fiber = nextEffect;
|
||||
const firstChild = fiber.child;
|
||||
|
||||
// TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
|
||||
switch (fiber.tag) {
|
||||
case FunctionComponent:
|
||||
case ForwardRef:
|
||||
case MemoComponent:
|
||||
case SimpleMemoComponent: {
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
fiber.mode & ProfileMode
|
||||
) {
|
||||
try {
|
||||
startLayoutEffectTimer();
|
||||
commitHookEffectListUnmount(HookLayout, fiber, fiber.return);
|
||||
} finally {
|
||||
recordLayoutEffectDuration(fiber);
|
||||
}
|
||||
} else {
|
||||
commitHookEffectListUnmount(HookLayout, fiber, fiber.return);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ClassComponent: {
|
||||
// TODO (Offscreen) Check: flags & RefStatic
|
||||
safelyDetachRef(fiber, fiber.return);
|
||||
|
||||
const instance = fiber.stateNode;
|
||||
if (typeof instance.componentWillUnmount === 'function') {
|
||||
safelyCallComponentWillUnmount(fiber, fiber.return, instance);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HostComponent: {
|
||||
safelyDetachRef(fiber, fiber.return);
|
||||
break;
|
||||
}
|
||||
case OffscreenComponent: {
|
||||
// Check if this is a
|
||||
const isHidden = fiber.memoizedState !== null;
|
||||
if (isHidden) {
|
||||
// Nested Offscreen tree is already hidden. Don't disappear
|
||||
// its effects.
|
||||
disappearLayoutEffects_complete(subtreeRoot);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO (Offscreen) Check: subtreeFlags & LayoutStatic
|
||||
if (firstChild !== null) {
|
||||
firstChild.return = fiber;
|
||||
nextEffect = firstChild;
|
||||
} else {
|
||||
disappearLayoutEffects_complete(subtreeRoot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function disappearLayoutEffects_complete(subtreeRoot: Fiber) {
|
||||
while (nextEffect !== null) {
|
||||
const fiber = nextEffect;
|
||||
|
||||
if (fiber === subtreeRoot) {
|
||||
nextEffect = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const sibling = fiber.sibling;
|
||||
if (sibling !== null) {
|
||||
sibling.return = fiber.return;
|
||||
nextEffect = sibling;
|
||||
return;
|
||||
}
|
||||
|
||||
nextEffect = fiber.return;
|
||||
}
|
||||
}
|
||||
|
||||
function reappearLayoutEffects_begin(subtreeRoot: Fiber) {
|
||||
while (nextEffect !== null) {
|
||||
const fiber = nextEffect;
|
||||
@@ -2397,7 +2463,9 @@ function reappearLayoutEffects_begin(subtreeRoot: Fiber) {
|
||||
|
||||
// TODO (Offscreen) Check: subtreeFlags & LayoutStatic
|
||||
if (firstChild !== null) {
|
||||
ensureCorrectReturnPointer(firstChild, fiber);
|
||||
// This node may have been reused from a previous render, so we can't
|
||||
// assume its return pointer is correct.
|
||||
firstChild.return = fiber;
|
||||
nextEffect = firstChild;
|
||||
} else {
|
||||
reappearLayoutEffects_complete(subtreeRoot);
|
||||
@@ -2426,7 +2494,9 @@ function reappearLayoutEffects_complete(subtreeRoot: Fiber) {
|
||||
|
||||
const sibling = fiber.sibling;
|
||||
if (sibling !== null) {
|
||||
ensureCorrectReturnPointer(sibling, fiber.return);
|
||||
// This node may have been reused from a previous render, so we can't
|
||||
// assume its return pointer is correct.
|
||||
sibling.return = fiber.return;
|
||||
nextEffect = sibling;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -980,12 +980,6 @@ function reappearLayoutEffectsOnFiber(node: Fiber) {
|
||||
}
|
||||
|
||||
function hideOrUnhideAllChildren(finishedWork, isHidden) {
|
||||
// Suspense layout effects semantics don't change for legacy roots.
|
||||
const isModernRoot = (finishedWork.mode & ConcurrentMode) !== NoMode;
|
||||
|
||||
const current = finishedWork.alternate;
|
||||
const wasHidden = current !== null && current.memoizedState !== null;
|
||||
|
||||
// Only hide or unhide the top-most host nodes.
|
||||
let hostSubtreeRoot = null;
|
||||
|
||||
@@ -1005,22 +999,6 @@ function hideOrUnhideAllChildren(finishedWork, isHidden) {
|
||||
unhideInstance(node.stateNode, node.memoizedProps);
|
||||
}
|
||||
}
|
||||
|
||||
if (enableSuspenseLayoutEffectSemantics && isModernRoot) {
|
||||
// This method is called during mutation; it should detach refs within a hidden subtree.
|
||||
// Attaching refs should be done elsewhere though (during layout).
|
||||
// TODO (Offscreen) Also check: flags & RefStatic
|
||||
if (isHidden) {
|
||||
safelyDetachRef(node, finishedWork);
|
||||
}
|
||||
|
||||
// TODO (Offscreen) Also check: subtreeFlags & (RefStatic | LayoutStatic)
|
||||
if (node.child !== null) {
|
||||
node.child.return = node;
|
||||
node = node.child;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (node.tag === HostText) {
|
||||
if (hostSubtreeRoot === null) {
|
||||
const instance = node.stateNode;
|
||||
@@ -1038,52 +1016,6 @@ function hideOrUnhideAllChildren(finishedWork, isHidden) {
|
||||
) {
|
||||
// Found a nested Offscreen component that is hidden.
|
||||
// Don't search any deeper. This tree should remain hidden.
|
||||
} else if (enableSuspenseLayoutEffectSemantics && isModernRoot) {
|
||||
// When a mounted Suspense subtree gets hidden again, destroy any nested layout effects.
|
||||
// TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
|
||||
switch (node.tag) {
|
||||
case FunctionComponent:
|
||||
case ForwardRef:
|
||||
case MemoComponent:
|
||||
case SimpleMemoComponent: {
|
||||
// Note that refs are attached by the useImperativeHandle() hook, not by commitAttachRef()
|
||||
if (isHidden && !wasHidden) {
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
node.mode & ProfileMode
|
||||
) {
|
||||
try {
|
||||
startLayoutEffectTimer();
|
||||
commitHookEffectListUnmount(HookLayout, node, finishedWork);
|
||||
} finally {
|
||||
recordLayoutEffectDuration(node);
|
||||
}
|
||||
} else {
|
||||
commitHookEffectListUnmount(HookLayout, node, finishedWork);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ClassComponent: {
|
||||
if (isHidden && !wasHidden) {
|
||||
// TODO (Offscreen) Check: flags & RefStatic
|
||||
safelyDetachRef(node, finishedWork);
|
||||
|
||||
const instance = node.stateNode;
|
||||
if (typeof instance.componentWillUnmount === 'function') {
|
||||
safelyCallComponentWillUnmount(node, finishedWork, instance);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (node.child !== null) {
|
||||
node.child.return = node;
|
||||
node = node.child;
|
||||
continue;
|
||||
}
|
||||
} else if (node.child !== null) {
|
||||
node.child.return = node;
|
||||
node = node.child;
|
||||
@@ -1801,6 +1733,11 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
|
||||
// This prevents sibling component effects from interfering with each other,
|
||||
// e.g. a destroy function in one component should never override a ref set
|
||||
// by a create function in another component during the same commit.
|
||||
// TODO: Check if we're inside an Offscreen subtree that disappeared
|
||||
// during this commit. If so, we would have already unmounted its
|
||||
// layout hooks. (However, since we null out the `destroy` function
|
||||
// right before calling it, the behavior is already correct, so this
|
||||
// would mostly be for modeling purposes.)
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
@@ -2183,20 +2120,36 @@ function commitMutationEffectsOnFiber(finishedWork: Fiber, root: FiberRoot) {
|
||||
switch (finishedWork.tag) {
|
||||
case SuspenseComponent: {
|
||||
const newState: OffscreenState | null = finishedWork.memoizedState;
|
||||
if (newState !== null) {
|
||||
markCommitTimeOfFallback();
|
||||
// Hide the Offscreen component that contains the primary children.
|
||||
// TODO: Ideally, this effect would have been scheduled on the
|
||||
// Offscreen fiber itself. That's how unhiding works: the Offscreen
|
||||
// component schedules an effect on itself. However, in this case, the
|
||||
// component didn't complete, so the fiber was never added to the
|
||||
// effect list in the normal path. We could have appended it to the
|
||||
// effect list in the Suspense component's second pass, but doing it
|
||||
// this way is less complicated. This would be simpler if we got rid
|
||||
// of the effect list and traversed the tree, like we're planning to
|
||||
// do.
|
||||
const primaryChildParent: Fiber = (finishedWork.child: any);
|
||||
hideOrUnhideAllChildren(primaryChildParent, true);
|
||||
const isHidden = newState !== null;
|
||||
const current = finishedWork.alternate;
|
||||
const wasHidden = current !== null && current.memoizedState !== null;
|
||||
const offscreenBoundary: Fiber = (finishedWork.child: any);
|
||||
|
||||
if (isHidden) {
|
||||
if (!wasHidden) {
|
||||
markCommitTimeOfFallback();
|
||||
if (supportsMutation) {
|
||||
hideOrUnhideAllChildren(offscreenBoundary, true);
|
||||
}
|
||||
if (
|
||||
enableSuspenseLayoutEffectSemantics &&
|
||||
(offscreenBoundary.mode & ConcurrentMode) !== NoMode
|
||||
) {
|
||||
let offscreenChild = offscreenBoundary.child;
|
||||
while (offscreenChild !== null) {
|
||||
nextEffect = offscreenChild;
|
||||
disappearLayoutEffects_begin(offscreenChild);
|
||||
offscreenChild = offscreenChild.sibling;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (wasHidden) {
|
||||
if (supportsMutation) {
|
||||
hideOrUnhideAllChildren(offscreenBoundary, false);
|
||||
}
|
||||
// TODO: Move re-appear call here for symmetry?
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2204,7 +2157,36 @@ function commitMutationEffectsOnFiber(finishedWork: Fiber, root: FiberRoot) {
|
||||
case LegacyHiddenComponent: {
|
||||
const newState: OffscreenState | null = finishedWork.memoizedState;
|
||||
const isHidden = newState !== null;
|
||||
hideOrUnhideAllChildren(finishedWork, isHidden);
|
||||
const current = finishedWork.alternate;
|
||||
const wasHidden = current !== null && current.memoizedState !== null;
|
||||
const offscreenBoundary: Fiber = finishedWork;
|
||||
|
||||
if (supportsMutation) {
|
||||
// TODO: This needs to run whenever there's an insertion or update
|
||||
// inside a hidden Offscreen tree.
|
||||
hideOrUnhideAllChildren(offscreenBoundary, isHidden);
|
||||
}
|
||||
|
||||
if (isHidden) {
|
||||
if (!wasHidden) {
|
||||
if (
|
||||
enableSuspenseLayoutEffectSemantics &&
|
||||
(offscreenBoundary.mode & ConcurrentMode) !== NoMode
|
||||
) {
|
||||
nextEffect = offscreenBoundary;
|
||||
let offscreenChild = offscreenBoundary.child;
|
||||
while (offscreenChild !== null) {
|
||||
nextEffect = offscreenChild;
|
||||
disappearLayoutEffects_begin(offscreenChild);
|
||||
offscreenChild = offscreenChild.sibling;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (wasHidden) {
|
||||
// TODO: Move re-appear call here for symmetry?
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2381,6 +2363,90 @@ function commitLayoutMountEffects_complete(
|
||||
}
|
||||
}
|
||||
|
||||
function disappearLayoutEffects_begin(subtreeRoot: Fiber) {
|
||||
while (nextEffect !== null) {
|
||||
const fiber = nextEffect;
|
||||
const firstChild = fiber.child;
|
||||
|
||||
// TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
|
||||
switch (fiber.tag) {
|
||||
case FunctionComponent:
|
||||
case ForwardRef:
|
||||
case MemoComponent:
|
||||
case SimpleMemoComponent: {
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
fiber.mode & ProfileMode
|
||||
) {
|
||||
try {
|
||||
startLayoutEffectTimer();
|
||||
commitHookEffectListUnmount(HookLayout, fiber, fiber.return);
|
||||
} finally {
|
||||
recordLayoutEffectDuration(fiber);
|
||||
}
|
||||
} else {
|
||||
commitHookEffectListUnmount(HookLayout, fiber, fiber.return);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ClassComponent: {
|
||||
// TODO (Offscreen) Check: flags & RefStatic
|
||||
safelyDetachRef(fiber, fiber.return);
|
||||
|
||||
const instance = fiber.stateNode;
|
||||
if (typeof instance.componentWillUnmount === 'function') {
|
||||
safelyCallComponentWillUnmount(fiber, fiber.return, instance);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HostComponent: {
|
||||
safelyDetachRef(fiber, fiber.return);
|
||||
break;
|
||||
}
|
||||
case OffscreenComponent: {
|
||||
// Check if this is a
|
||||
const isHidden = fiber.memoizedState !== null;
|
||||
if (isHidden) {
|
||||
// Nested Offscreen tree is already hidden. Don't disappear
|
||||
// its effects.
|
||||
disappearLayoutEffects_complete(subtreeRoot);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO (Offscreen) Check: subtreeFlags & LayoutStatic
|
||||
if (firstChild !== null) {
|
||||
firstChild.return = fiber;
|
||||
nextEffect = firstChild;
|
||||
} else {
|
||||
disappearLayoutEffects_complete(subtreeRoot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function disappearLayoutEffects_complete(subtreeRoot: Fiber) {
|
||||
while (nextEffect !== null) {
|
||||
const fiber = nextEffect;
|
||||
|
||||
if (fiber === subtreeRoot) {
|
||||
nextEffect = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const sibling = fiber.sibling;
|
||||
if (sibling !== null) {
|
||||
sibling.return = fiber.return;
|
||||
nextEffect = sibling;
|
||||
return;
|
||||
}
|
||||
|
||||
nextEffect = fiber.return;
|
||||
}
|
||||
}
|
||||
|
||||
function reappearLayoutEffects_begin(subtreeRoot: Fiber) {
|
||||
while (nextEffect !== null) {
|
||||
const fiber = nextEffect;
|
||||
@@ -2397,7 +2463,9 @@ function reappearLayoutEffects_begin(subtreeRoot: Fiber) {
|
||||
|
||||
// TODO (Offscreen) Check: subtreeFlags & LayoutStatic
|
||||
if (firstChild !== null) {
|
||||
ensureCorrectReturnPointer(firstChild, fiber);
|
||||
// This node may have been reused from a previous render, so we can't
|
||||
// assume its return pointer is correct.
|
||||
firstChild.return = fiber;
|
||||
nextEffect = firstChild;
|
||||
} else {
|
||||
reappearLayoutEffects_complete(subtreeRoot);
|
||||
@@ -2426,7 +2494,9 @@ function reappearLayoutEffects_complete(subtreeRoot: Fiber) {
|
||||
|
||||
const sibling = fiber.sibling;
|
||||
if (sibling !== null) {
|
||||
ensureCorrectReturnPointer(sibling, fiber.return);
|
||||
// This node may have been reused from a previous render, so we can't
|
||||
// assume its return pointer is correct.
|
||||
sibling.return = fiber.return;
|
||||
nextEffect = sibling;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -220,7 +220,6 @@ describe('ReactOffscreen', () => {
|
||||
|
||||
// @gate experimental || www
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate !persistent
|
||||
it('mounts/unmounts layout effects when visibility changes (starting visible)', async () => {
|
||||
function Child({text}) {
|
||||
useLayoutEffect(() => {
|
||||
@@ -271,7 +270,6 @@ describe('ReactOffscreen', () => {
|
||||
|
||||
// @gate experimental || www
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate !persistent
|
||||
it('mounts/unmounts layout effects when visibility changes (starting hidden)', async () => {
|
||||
function Child({text}) {
|
||||
useLayoutEffect(() => {
|
||||
|
||||
-20
@@ -567,7 +567,6 @@ describe('ReactSuspenseEffectsSemantics', () => {
|
||||
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate enableCache
|
||||
// @gate !persistent
|
||||
it('should be destroyed and recreated for function components', async () => {
|
||||
function App({children = null}) {
|
||||
Scheduler.unstable_yieldValue('App render');
|
||||
@@ -697,7 +696,6 @@ describe('ReactSuspenseEffectsSemantics', () => {
|
||||
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate enableCache
|
||||
// @gate !persistent
|
||||
it('should be destroyed and recreated for class components', async () => {
|
||||
class ClassText extends React.Component {
|
||||
componentDidMount() {
|
||||
@@ -843,7 +841,6 @@ describe('ReactSuspenseEffectsSemantics', () => {
|
||||
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate enableCache
|
||||
// @gate !persistent
|
||||
it('should be destroyed and recreated when nested below host components', async () => {
|
||||
function App({children = null}) {
|
||||
Scheduler.unstable_yieldValue('App render');
|
||||
@@ -954,7 +951,6 @@ describe('ReactSuspenseEffectsSemantics', () => {
|
||||
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate enableCache
|
||||
// @gate !persistent
|
||||
it('should be destroyed and recreated even if there is a bailout because of memoization', async () => {
|
||||
const MemoizedText = React.memo(Text, () => true);
|
||||
|
||||
@@ -1071,7 +1067,6 @@ describe('ReactSuspenseEffectsSemantics', () => {
|
||||
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate enableCache
|
||||
// @gate !persistent
|
||||
it('should respect nested suspense boundaries', async () => {
|
||||
function App({innerChildren = null, outerChildren = null}) {
|
||||
return (
|
||||
@@ -1295,7 +1290,6 @@ describe('ReactSuspenseEffectsSemantics', () => {
|
||||
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate enableCache
|
||||
// @gate !persistent
|
||||
it('should show nested host nodes if multiple boundaries resolve at the same time', async () => {
|
||||
function App({innerChildren = null, outerChildren = null}) {
|
||||
return (
|
||||
@@ -1406,7 +1400,6 @@ describe('ReactSuspenseEffectsSemantics', () => {
|
||||
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate enableCache
|
||||
// @gate !persistent
|
||||
it('should be cleaned up inside of a fallback that suspends', async () => {
|
||||
function App({fallbackChildren = null, outerChildren = null}) {
|
||||
return (
|
||||
@@ -1550,7 +1543,6 @@ describe('ReactSuspenseEffectsSemantics', () => {
|
||||
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate enableCache
|
||||
// @gate !persistent
|
||||
it('should be cleaned up inside of a fallback that suspends (alternate)', async () => {
|
||||
function App({fallbackChildren = null, outerChildren = null}) {
|
||||
return (
|
||||
@@ -1671,7 +1663,6 @@ describe('ReactSuspenseEffectsSemantics', () => {
|
||||
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate enableCache
|
||||
// @gate !persistent
|
||||
it('should be cleaned up deeper inside of a subtree that suspends', async () => {
|
||||
function ConditionalSuspense({shouldSuspend}) {
|
||||
if (shouldSuspend) {
|
||||
@@ -1755,7 +1746,6 @@ describe('ReactSuspenseEffectsSemantics', () => {
|
||||
describe('that throw errors', () => {
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate enableCache
|
||||
// @gate !persistent
|
||||
it('are properly handled for componentDidMount', async () => {
|
||||
let componentDidMountShouldThrow = false;
|
||||
|
||||
@@ -1895,7 +1885,6 @@ describe('ReactSuspenseEffectsSemantics', () => {
|
||||
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate enableCache
|
||||
// @gate !persistent
|
||||
it('are properly handled for componentWillUnmount', async () => {
|
||||
class ThrowsInWillUnmount extends React.Component {
|
||||
componentDidMount() {
|
||||
@@ -2009,7 +1998,6 @@ describe('ReactSuspenseEffectsSemantics', () => {
|
||||
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate enableCache
|
||||
// @gate !persistent
|
||||
// @gate replayFailedUnitOfWorkWithInvokeGuardedCallback
|
||||
it('are properly handled for layout effect creation', async () => {
|
||||
let useLayoutEffectShouldThrow = false;
|
||||
@@ -2150,7 +2138,6 @@ describe('ReactSuspenseEffectsSemantics', () => {
|
||||
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate enableCache
|
||||
// @gate !persistent
|
||||
// @gate replayFailedUnitOfWorkWithInvokeGuardedCallback
|
||||
it('are properly handled for layout effect descruction', async () => {
|
||||
function ThrowsInLayoutEffectDestroy() {
|
||||
@@ -2263,7 +2250,6 @@ describe('ReactSuspenseEffectsSemantics', () => {
|
||||
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate enableCache
|
||||
// @gate !persistent
|
||||
it('should be only destroy layout effects once if a tree suspends in multiple places', async () => {
|
||||
class ClassText extends React.Component {
|
||||
componentDidMount() {
|
||||
@@ -2403,7 +2389,6 @@ describe('ReactSuspenseEffectsSemantics', () => {
|
||||
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate enableCache
|
||||
// @gate !persistent
|
||||
it('should be only destroy layout effects once if a component suspends multiple times', async () => {
|
||||
class ClassText extends React.Component {
|
||||
componentDidMount() {
|
||||
@@ -2688,7 +2673,6 @@ describe('ReactSuspenseEffectsSemantics', () => {
|
||||
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate enableCache
|
||||
// @gate !persistent
|
||||
it('should be cleared and reset for host components', async () => {
|
||||
function App({children}) {
|
||||
Scheduler.unstable_yieldValue(`App render`);
|
||||
@@ -2786,7 +2770,6 @@ describe('ReactSuspenseEffectsSemantics', () => {
|
||||
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate enableCache
|
||||
// @gate !persistent
|
||||
it('should be cleared and reset for class components', async () => {
|
||||
class ClassComponent extends React.Component {
|
||||
render() {
|
||||
@@ -2888,7 +2871,6 @@ describe('ReactSuspenseEffectsSemantics', () => {
|
||||
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate enableCache
|
||||
// @gate !persistent
|
||||
it('should be cleared and reset for function components with useImperativeHandle', async () => {
|
||||
const FunctionComponent = React.forwardRef((props, ref) => {
|
||||
Scheduler.unstable_yieldValue('FunctionComponent render');
|
||||
@@ -2994,7 +2976,6 @@ describe('ReactSuspenseEffectsSemantics', () => {
|
||||
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate enableCache
|
||||
// @gate !persistent
|
||||
it('should not reset for user-managed values', async () => {
|
||||
function RefChecker({forwardedRef}) {
|
||||
Scheduler.unstable_yieldValue(`RefChecker render`);
|
||||
@@ -3093,7 +3074,6 @@ describe('ReactSuspenseEffectsSemantics', () => {
|
||||
describe('that throw errors', () => {
|
||||
// @gate enableSuspenseLayoutEffectSemantics
|
||||
// @gate enableCache
|
||||
// @gate !persistent
|
||||
// @gate replayFailedUnitOfWorkWithInvokeGuardedCallback
|
||||
it('are properly handled in ref callbacks', async () => {
|
||||
let useRefCallbackShouldThrow = false;
|
||||
|
||||
Reference in New Issue
Block a user