Skip to content

Commit 697702b

Browse files
committed
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.
1 parent 9929119 commit 697702b

File tree

2 files changed

+142
-156
lines changed

2 files changed

+142
-156
lines changed

packages/react-reconciler/src/ReactFiberCommitWork.new.js

Lines changed: 71 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -2565,13 +2565,8 @@ function commitMutationEffectsOnFiber(
25652565
if (isHidden) {
25662566
if (!wasHidden) {
25672567
if ((offscreenBoundary.mode & ConcurrentMode) !== NoMode) {
2568-
nextEffect = offscreenBoundary;
2569-
let offscreenChild = offscreenBoundary.child;
2570-
while (offscreenChild !== null) {
2571-
nextEffect = offscreenChild;
2572-
disappearLayoutEffects_begin(offscreenChild);
2573-
offscreenChild = offscreenChild.sibling;
2574-
}
2568+
// Disappear the layout effects of all the children
2569+
recursivelyTraverseDisappearLayoutEffects(offscreenBoundary);
25752570
}
25762571
}
25772572
} else {
@@ -2696,87 +2691,85 @@ function recursivelyTraverseLayoutEffects(
26962691
setCurrentDebugFiberInDEV(prevDebugFiber);
26972692
}
26982693

2699-
function disappearLayoutEffects_begin(subtreeRoot: Fiber) {
2700-
while (nextEffect !== null) {
2701-
const fiber = nextEffect;
2702-
const firstChild = fiber.child;
2703-
2704-
// TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
2705-
switch (fiber.tag) {
2706-
case FunctionComponent:
2707-
case ForwardRef:
2708-
case MemoComponent:
2709-
case SimpleMemoComponent: {
2710-
if (
2711-
enableProfilerTimer &&
2712-
enableProfilerCommitHooks &&
2713-
fiber.mode & ProfileMode
2714-
) {
2715-
try {
2716-
startLayoutEffectTimer();
2717-
commitHookEffectListUnmount(HookLayout, fiber, fiber.return);
2718-
} finally {
2719-
recordLayoutEffectDuration(fiber);
2720-
}
2721-
} else {
2722-
commitHookEffectListUnmount(HookLayout, fiber, fiber.return);
2723-
}
2724-
break;
2725-
}
2726-
case ClassComponent: {
2727-
// TODO (Offscreen) Check: flags & RefStatic
2728-
safelyDetachRef(fiber, fiber.return);
2729-
2730-
const instance = fiber.stateNode;
2731-
if (typeof instance.componentWillUnmount === 'function') {
2732-
safelyCallComponentWillUnmount(fiber, fiber.return, instance);
2733-
}
2734-
break;
2735-
}
2736-
case HostComponent: {
2737-
safelyDetachRef(fiber, fiber.return);
2738-
break;
2739-
}
2740-
case OffscreenComponent: {
2741-
// Check if this is a
2742-
const isHidden = fiber.memoizedState !== null;
2743-
if (isHidden) {
2744-
// Nested Offscreen tree is already hidden. Don't disappear
2745-
// its effects.
2746-
disappearLayoutEffects_complete(subtreeRoot);
2747-
continue;
2694+
function disappearLayoutEffects(finishedWork: Fiber) {
2695+
switch (finishedWork.tag) {
2696+
case FunctionComponent:
2697+
case ForwardRef:
2698+
case MemoComponent:
2699+
case SimpleMemoComponent: {
2700+
// TODO (Offscreen) Check: flags & LayoutStatic
2701+
if (
2702+
enableProfilerTimer &&
2703+
enableProfilerCommitHooks &&
2704+
finishedWork.mode & ProfileMode
2705+
) {
2706+
try {
2707+
startLayoutEffectTimer();
2708+
commitHookEffectListUnmount(
2709+
HookLayout,
2710+
finishedWork,
2711+
finishedWork.return,
2712+
);
2713+
} finally {
2714+
recordLayoutEffectDuration(finishedWork);
27482715
}
2749-
break;
2716+
} else {
2717+
commitHookEffectListUnmount(
2718+
HookLayout,
2719+
finishedWork,
2720+
finishedWork.return,
2721+
);
27502722
}
2751-
}
27522723

2753-
// TODO (Offscreen) Check: subtreeFlags & LayoutStatic
2754-
if (firstChild !== null) {
2755-
firstChild.return = fiber;
2756-
nextEffect = firstChild;
2757-
} else {
2758-
disappearLayoutEffects_complete(subtreeRoot);
2724+
recursivelyTraverseDisappearLayoutEffects(finishedWork);
2725+
break;
27592726
}
2760-
}
2761-
}
2727+
case ClassComponent: {
2728+
// TODO (Offscreen) Check: flags & RefStatic
2729+
safelyDetachRef(finishedWork, finishedWork.return);
27622730

2763-
function disappearLayoutEffects_complete(subtreeRoot: Fiber) {
2764-
while (nextEffect !== null) {
2765-
const fiber = nextEffect;
2731+
const instance = finishedWork.stateNode;
2732+
if (typeof instance.componentWillUnmount === 'function') {
2733+
safelyCallComponentWillUnmount(
2734+
finishedWork,
2735+
finishedWork.return,
2736+
instance,
2737+
);
2738+
}
27662739

2767-
if (fiber === subtreeRoot) {
2768-
nextEffect = null;
2769-
return;
2740+
recursivelyTraverseDisappearLayoutEffects(finishedWork);
2741+
break;
27702742
}
2743+
case HostComponent: {
2744+
// TODO (Offscreen) Check: flags & RefStatic
2745+
safelyDetachRef(finishedWork, finishedWork.return);
27712746

2772-
const sibling = fiber.sibling;
2773-
if (sibling !== null) {
2774-
sibling.return = fiber.return;
2775-
nextEffect = sibling;
2776-
return;
2747+
recursivelyTraverseDisappearLayoutEffects(finishedWork);
2748+
break;
27772749
}
2750+
case OffscreenComponent: {
2751+
const isHidden = finishedWork.memoizedState !== null;
2752+
if (isHidden) {
2753+
// Nested Offscreen tree is already hidden. Don't disappear
2754+
// its effects.
2755+
} else {
2756+
recursivelyTraverseDisappearLayoutEffects(finishedWork);
2757+
}
2758+
break;
2759+
}
2760+
default: {
2761+
recursivelyTraverseDisappearLayoutEffects(finishedWork);
2762+
break;
2763+
}
2764+
}
2765+
}
27782766

2779-
nextEffect = fiber.return;
2767+
function recursivelyTraverseDisappearLayoutEffects(parentFiber: Fiber) {
2768+
// TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
2769+
let child = parentFiber.child;
2770+
while (child !== null) {
2771+
disappearLayoutEffects(child);
2772+
child = child.sibling;
27802773
}
27812774
}
27822775

packages/react-reconciler/src/ReactFiberCommitWork.old.js

Lines changed: 71 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -2565,13 +2565,8 @@ function commitMutationEffectsOnFiber(
25652565
if (isHidden) {
25662566
if (!wasHidden) {
25672567
if ((offscreenBoundary.mode & ConcurrentMode) !== NoMode) {
2568-
nextEffect = offscreenBoundary;
2569-
let offscreenChild = offscreenBoundary.child;
2570-
while (offscreenChild !== null) {
2571-
nextEffect = offscreenChild;
2572-
disappearLayoutEffects_begin(offscreenChild);
2573-
offscreenChild = offscreenChild.sibling;
2574-
}
2568+
// Disappear the layout effects of all the children
2569+
recursivelyTraverseDisappearLayoutEffects(offscreenBoundary);
25752570
}
25762571
}
25772572
} else {
@@ -2696,87 +2691,85 @@ function recursivelyTraverseLayoutEffects(
26962691
setCurrentDebugFiberInDEV(prevDebugFiber);
26972692
}
26982693

2699-
function disappearLayoutEffects_begin(subtreeRoot: Fiber) {
2700-
while (nextEffect !== null) {
2701-
const fiber = nextEffect;
2702-
const firstChild = fiber.child;
2703-
2704-
// TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
2705-
switch (fiber.tag) {
2706-
case FunctionComponent:
2707-
case ForwardRef:
2708-
case MemoComponent:
2709-
case SimpleMemoComponent: {
2710-
if (
2711-
enableProfilerTimer &&
2712-
enableProfilerCommitHooks &&
2713-
fiber.mode & ProfileMode
2714-
) {
2715-
try {
2716-
startLayoutEffectTimer();
2717-
commitHookEffectListUnmount(HookLayout, fiber, fiber.return);
2718-
} finally {
2719-
recordLayoutEffectDuration(fiber);
2720-
}
2721-
} else {
2722-
commitHookEffectListUnmount(HookLayout, fiber, fiber.return);
2723-
}
2724-
break;
2725-
}
2726-
case ClassComponent: {
2727-
// TODO (Offscreen) Check: flags & RefStatic
2728-
safelyDetachRef(fiber, fiber.return);
2729-
2730-
const instance = fiber.stateNode;
2731-
if (typeof instance.componentWillUnmount === 'function') {
2732-
safelyCallComponentWillUnmount(fiber, fiber.return, instance);
2733-
}
2734-
break;
2735-
}
2736-
case HostComponent: {
2737-
safelyDetachRef(fiber, fiber.return);
2738-
break;
2739-
}
2740-
case OffscreenComponent: {
2741-
// Check if this is a
2742-
const isHidden = fiber.memoizedState !== null;
2743-
if (isHidden) {
2744-
// Nested Offscreen tree is already hidden. Don't disappear
2745-
// its effects.
2746-
disappearLayoutEffects_complete(subtreeRoot);
2747-
continue;
2694+
function disappearLayoutEffects(finishedWork: Fiber) {
2695+
switch (finishedWork.tag) {
2696+
case FunctionComponent:
2697+
case ForwardRef:
2698+
case MemoComponent:
2699+
case SimpleMemoComponent: {
2700+
// TODO (Offscreen) Check: flags & LayoutStatic
2701+
if (
2702+
enableProfilerTimer &&
2703+
enableProfilerCommitHooks &&
2704+
finishedWork.mode & ProfileMode
2705+
) {
2706+
try {
2707+
startLayoutEffectTimer();
2708+
commitHookEffectListUnmount(
2709+
HookLayout,
2710+
finishedWork,
2711+
finishedWork.return,
2712+
);
2713+
} finally {
2714+
recordLayoutEffectDuration(finishedWork);
27482715
}
2749-
break;
2716+
} else {
2717+
commitHookEffectListUnmount(
2718+
HookLayout,
2719+
finishedWork,
2720+
finishedWork.return,
2721+
);
27502722
}
2751-
}
27522723

2753-
// TODO (Offscreen) Check: subtreeFlags & LayoutStatic
2754-
if (firstChild !== null) {
2755-
firstChild.return = fiber;
2756-
nextEffect = firstChild;
2757-
} else {
2758-
disappearLayoutEffects_complete(subtreeRoot);
2724+
recursivelyTraverseDisappearLayoutEffects(finishedWork);
2725+
break;
27592726
}
2760-
}
2761-
}
2727+
case ClassComponent: {
2728+
// TODO (Offscreen) Check: flags & RefStatic
2729+
safelyDetachRef(finishedWork, finishedWork.return);
27622730

2763-
function disappearLayoutEffects_complete(subtreeRoot: Fiber) {
2764-
while (nextEffect !== null) {
2765-
const fiber = nextEffect;
2731+
const instance = finishedWork.stateNode;
2732+
if (typeof instance.componentWillUnmount === 'function') {
2733+
safelyCallComponentWillUnmount(
2734+
finishedWork,
2735+
finishedWork.return,
2736+
instance,
2737+
);
2738+
}
27662739

2767-
if (fiber === subtreeRoot) {
2768-
nextEffect = null;
2769-
return;
2740+
recursivelyTraverseDisappearLayoutEffects(finishedWork);
2741+
break;
27702742
}
2743+
case HostComponent: {
2744+
// TODO (Offscreen) Check: flags & RefStatic
2745+
safelyDetachRef(finishedWork, finishedWork.return);
27712746

2772-
const sibling = fiber.sibling;
2773-
if (sibling !== null) {
2774-
sibling.return = fiber.return;
2775-
nextEffect = sibling;
2776-
return;
2747+
recursivelyTraverseDisappearLayoutEffects(finishedWork);
2748+
break;
27772749
}
2750+
case OffscreenComponent: {
2751+
const isHidden = finishedWork.memoizedState !== null;
2752+
if (isHidden) {
2753+
// Nested Offscreen tree is already hidden. Don't disappear
2754+
// its effects.
2755+
} else {
2756+
recursivelyTraverseDisappearLayoutEffects(finishedWork);
2757+
}
2758+
break;
2759+
}
2760+
default: {
2761+
recursivelyTraverseDisappearLayoutEffects(finishedWork);
2762+
break;
2763+
}
2764+
}
2765+
}
27782766

2779-
nextEffect = fiber.return;
2767+
function recursivelyTraverseDisappearLayoutEffects(parentFiber: Fiber) {
2768+
// TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
2769+
let child = parentFiber.child;
2770+
while (child !== null) {
2771+
disappearLayoutEffects(child);
2772+
child = child.sibling;
27802773
}
27812774
}
27822775

0 commit comments

Comments
 (0)