mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Use recursion to traverse during "disappear layout" phase
This converts the "disappear layout" phase to iterate over its effects
recursively instead of iteratively. This makes it easier to track
contextual information, like whether a fiber is inside a hidden tree.
We already made this change for several other phases, like mutation and
layout mount. See 481dece for more context.
This commit is contained in:
@@ -2565,13 +2565,8 @@ function commitMutationEffectsOnFiber(
|
||||
if (isHidden) {
|
||||
if (!wasHidden) {
|
||||
if ((offscreenBoundary.mode & ConcurrentMode) !== NoMode) {
|
||||
nextEffect = offscreenBoundary;
|
||||
let offscreenChild = offscreenBoundary.child;
|
||||
while (offscreenChild !== null) {
|
||||
nextEffect = offscreenChild;
|
||||
disappearLayoutEffects_begin(offscreenChild);
|
||||
offscreenChild = offscreenChild.sibling;
|
||||
}
|
||||
// Disappear the layout effects of all the children
|
||||
recursivelyTraverseDisappearLayoutEffects(offscreenBoundary);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -2696,87 +2691,85 @@ function recursivelyTraverseLayoutEffects(
|
||||
setCurrentDebugFiberInDEV(prevDebugFiber);
|
||||
}
|
||||
|
||||
function disappearLayoutEffects_begin(subtreeRoot: Fiber) {
|
||||
while (nextEffect !== null) {
|
||||
const fiber = nextEffect;
|
||||
const firstChild = fiber.child;
|
||||
function disappearLayoutEffects(finishedWork: Fiber) {
|
||||
switch (finishedWork.tag) {
|
||||
case FunctionComponent:
|
||||
case ForwardRef:
|
||||
case MemoComponent:
|
||||
case SimpleMemoComponent: {
|
||||
// TODO (Offscreen) Check: flags & LayoutStatic
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
finishedWork.mode & ProfileMode
|
||||
) {
|
||||
try {
|
||||
startLayoutEffectTimer();
|
||||
commitHookEffectListUnmount(
|
||||
HookLayout,
|
||||
finishedWork,
|
||||
finishedWork.return,
|
||||
);
|
||||
} finally {
|
||||
recordLayoutEffectDuration(finishedWork);
|
||||
}
|
||||
} else {
|
||||
commitHookEffectListUnmount(
|
||||
HookLayout,
|
||||
finishedWork,
|
||||
finishedWork.return,
|
||||
);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
recursivelyTraverseDisappearLayoutEffects(finishedWork);
|
||||
break;
|
||||
}
|
||||
case ClassComponent: {
|
||||
// TODO (Offscreen) Check: flags & RefStatic
|
||||
safelyDetachRef(finishedWork, finishedWork.return);
|
||||
|
||||
// TODO (Offscreen) Check: subtreeFlags & LayoutStatic
|
||||
if (firstChild !== null) {
|
||||
firstChild.return = fiber;
|
||||
nextEffect = firstChild;
|
||||
} else {
|
||||
disappearLayoutEffects_complete(subtreeRoot);
|
||||
const instance = finishedWork.stateNode;
|
||||
if (typeof instance.componentWillUnmount === 'function') {
|
||||
safelyCallComponentWillUnmount(
|
||||
finishedWork,
|
||||
finishedWork.return,
|
||||
instance,
|
||||
);
|
||||
}
|
||||
|
||||
recursivelyTraverseDisappearLayoutEffects(finishedWork);
|
||||
break;
|
||||
}
|
||||
case HostComponent: {
|
||||
// TODO (Offscreen) Check: flags & RefStatic
|
||||
safelyDetachRef(finishedWork, finishedWork.return);
|
||||
|
||||
recursivelyTraverseDisappearLayoutEffects(finishedWork);
|
||||
break;
|
||||
}
|
||||
case OffscreenComponent: {
|
||||
const isHidden = finishedWork.memoizedState !== null;
|
||||
if (isHidden) {
|
||||
// Nested Offscreen tree is already hidden. Don't disappear
|
||||
// its effects.
|
||||
} else {
|
||||
recursivelyTraverseDisappearLayoutEffects(finishedWork);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
recursivelyTraverseDisappearLayoutEffects(finishedWork);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 recursivelyTraverseDisappearLayoutEffects(parentFiber: Fiber) {
|
||||
// TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
|
||||
let child = parentFiber.child;
|
||||
while (child !== null) {
|
||||
disappearLayoutEffects(child);
|
||||
child = child.sibling;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2565,13 +2565,8 @@ function commitMutationEffectsOnFiber(
|
||||
if (isHidden) {
|
||||
if (!wasHidden) {
|
||||
if ((offscreenBoundary.mode & ConcurrentMode) !== NoMode) {
|
||||
nextEffect = offscreenBoundary;
|
||||
let offscreenChild = offscreenBoundary.child;
|
||||
while (offscreenChild !== null) {
|
||||
nextEffect = offscreenChild;
|
||||
disappearLayoutEffects_begin(offscreenChild);
|
||||
offscreenChild = offscreenChild.sibling;
|
||||
}
|
||||
// Disappear the layout effects of all the children
|
||||
recursivelyTraverseDisappearLayoutEffects(offscreenBoundary);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -2696,87 +2691,85 @@ function recursivelyTraverseLayoutEffects(
|
||||
setCurrentDebugFiberInDEV(prevDebugFiber);
|
||||
}
|
||||
|
||||
function disappearLayoutEffects_begin(subtreeRoot: Fiber) {
|
||||
while (nextEffect !== null) {
|
||||
const fiber = nextEffect;
|
||||
const firstChild = fiber.child;
|
||||
function disappearLayoutEffects(finishedWork: Fiber) {
|
||||
switch (finishedWork.tag) {
|
||||
case FunctionComponent:
|
||||
case ForwardRef:
|
||||
case MemoComponent:
|
||||
case SimpleMemoComponent: {
|
||||
// TODO (Offscreen) Check: flags & LayoutStatic
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
finishedWork.mode & ProfileMode
|
||||
) {
|
||||
try {
|
||||
startLayoutEffectTimer();
|
||||
commitHookEffectListUnmount(
|
||||
HookLayout,
|
||||
finishedWork,
|
||||
finishedWork.return,
|
||||
);
|
||||
} finally {
|
||||
recordLayoutEffectDuration(finishedWork);
|
||||
}
|
||||
} else {
|
||||
commitHookEffectListUnmount(
|
||||
HookLayout,
|
||||
finishedWork,
|
||||
finishedWork.return,
|
||||
);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
recursivelyTraverseDisappearLayoutEffects(finishedWork);
|
||||
break;
|
||||
}
|
||||
case ClassComponent: {
|
||||
// TODO (Offscreen) Check: flags & RefStatic
|
||||
safelyDetachRef(finishedWork, finishedWork.return);
|
||||
|
||||
// TODO (Offscreen) Check: subtreeFlags & LayoutStatic
|
||||
if (firstChild !== null) {
|
||||
firstChild.return = fiber;
|
||||
nextEffect = firstChild;
|
||||
} else {
|
||||
disappearLayoutEffects_complete(subtreeRoot);
|
||||
const instance = finishedWork.stateNode;
|
||||
if (typeof instance.componentWillUnmount === 'function') {
|
||||
safelyCallComponentWillUnmount(
|
||||
finishedWork,
|
||||
finishedWork.return,
|
||||
instance,
|
||||
);
|
||||
}
|
||||
|
||||
recursivelyTraverseDisappearLayoutEffects(finishedWork);
|
||||
break;
|
||||
}
|
||||
case HostComponent: {
|
||||
// TODO (Offscreen) Check: flags & RefStatic
|
||||
safelyDetachRef(finishedWork, finishedWork.return);
|
||||
|
||||
recursivelyTraverseDisappearLayoutEffects(finishedWork);
|
||||
break;
|
||||
}
|
||||
case OffscreenComponent: {
|
||||
const isHidden = finishedWork.memoizedState !== null;
|
||||
if (isHidden) {
|
||||
// Nested Offscreen tree is already hidden. Don't disappear
|
||||
// its effects.
|
||||
} else {
|
||||
recursivelyTraverseDisappearLayoutEffects(finishedWork);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
recursivelyTraverseDisappearLayoutEffects(finishedWork);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 recursivelyTraverseDisappearLayoutEffects(parentFiber: Fiber) {
|
||||
// TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
|
||||
let child = parentFiber.child;
|
||||
while (child !== null) {
|
||||
disappearLayoutEffects(child);
|
||||
child = child.sibling;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user