Skip to content

Commit 1cf59f3

Browse files
committedDec 18, 2020
Convert passive unmount phase to tree traversal
1 parent ab29695 commit 1cf59f3

6 files changed

+364
-178
lines changed
 

Diff for: ‎packages/react-reconciler/src/ReactFiber.new.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ export function resetWorkInProgress(workInProgress: Fiber, renderLanes: Lanes) {
363363
// We assume pendingProps, index, key, ref, return are still untouched to
364364
// avoid doing another reconciliation.
365365

366-
// Reset the effect tag but keep any Placement tags, since that's something
366+
// Reset the effect flags but keep any Placement tags, since that's something
367367
// that child fiber is setting, not the reconciliation.
368368
workInProgress.flags &= Placement;
369369

Diff for: ‎packages/react-reconciler/src/ReactFiber.old.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ export function resetWorkInProgress(workInProgress: Fiber, renderLanes: Lanes) {
363363
// We assume pendingProps, index, key, ref, return are still untouched to
364364
// avoid doing another reconciliation.
365365

366-
// Reset the effect tag but keep any Placement tags, since that's something
366+
// Reset the effect flags but keep any Placement tags, since that's something
367367
// that child fiber is setting, not the reconciliation.
368368
workInProgress.flags &= Placement;
369369

Diff for: ‎packages/react-reconciler/src/ReactFiberCommitWork.new.js

+155-3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import type {FunctionComponentUpdateQueue} from './ReactFiberHooks.new';
2424
import type {Wakeable} from 'shared/ReactTypes';
2525
import type {ReactPriorityLevel} from './ReactInternalTypes';
2626
import type {OffscreenState} from './ReactFiberOffscreenComponent';
27+
import type {HookFlags} from './ReactHookEffectTags';
2728

2829
import {unstable_wrap as Schedule_tracing_wrap} from 'scheduler/tracing';
2930
import {
@@ -65,10 +66,13 @@ import {
6566
NoFlags,
6667
ContentReset,
6768
Placement,
69+
ChildDeletion,
6870
Snapshot,
6971
Update,
7072
Passive,
73+
PassiveStatic,
7174
PassiveMask,
75+
PassiveUnmountPendingDev,
7276
} from './ReactFiberFlags';
7377
import getComponentName from 'shared/getComponentName';
7478
import invariant from 'shared/invariant';
@@ -340,19 +344,19 @@ function commitBeforeMutationLifeCycles(
340344
);
341345
}
342346

343-
function commitHookEffectListUnmount(tag: number, finishedWork: Fiber) {
347+
function commitHookEffectListUnmount(flags: HookFlags, finishedWork: Fiber) {
344348
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
345349
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
346350
if (lastEffect !== null) {
347351
const firstEffect = lastEffect.next;
348352
let effect = firstEffect;
349353
do {
350-
if ((effect.tag & tag) === tag) {
354+
if ((effect.tag & flags) === flags) {
351355
// Unmount
352356
const destroy = effect.destroy;
353357
effect.destroy = undefined;
354358
if (destroy !== undefined) {
355-
destroy();
359+
safelyCallDestroy(finishedWork, destroy);
356360
}
357361
}
358362
effect = effect.next;
@@ -1914,6 +1918,154 @@ function commitPassiveMountOnFiber(
19141918
}
19151919
}
19161920

1921+
export function commitPassiveUnmountEffects(firstChild: Fiber): void {
1922+
nextEffect = firstChild;
1923+
commitPassiveUnmountEffects_begin();
1924+
}
1925+
1926+
function commitPassiveUnmountEffects_begin() {
1927+
while (nextEffect !== null) {
1928+
const fiber = nextEffect;
1929+
const child = fiber.child;
1930+
1931+
if ((nextEffect.flags & ChildDeletion) !== NoFlags) {
1932+
const deletions = fiber.deletions;
1933+
if (deletions !== null) {
1934+
for (let i = 0; i < deletions.length; i++) {
1935+
const fiberToDelete = deletions[i];
1936+
nextEffect = fiberToDelete;
1937+
commitPassiveUnmountEffectsInsideOfDeletedTree_begin(fiberToDelete);
1938+
1939+
// Now that passive effects have been processed, it's safe to detach lingering pointers.
1940+
detachFiberAfterEffects(fiberToDelete);
1941+
}
1942+
nextEffect = fiber;
1943+
}
1944+
}
1945+
1946+
if ((fiber.subtreeFlags & PassiveMask) !== NoFlags && child !== null) {
1947+
ensureCorrectReturnPointer(child, fiber);
1948+
nextEffect = child;
1949+
} else {
1950+
commitPassiveUnmountEffects_complete();
1951+
}
1952+
}
1953+
}
1954+
1955+
function commitPassiveUnmountEffects_complete() {
1956+
while (nextEffect !== null) {
1957+
const fiber = nextEffect;
1958+
if ((fiber.flags & Passive) !== NoFlags) {
1959+
setCurrentDebugFiberInDEV(fiber);
1960+
commitPassiveUnmountOnFiber(fiber);
1961+
resetCurrentDebugFiberInDEV();
1962+
}
1963+
1964+
const sibling = fiber.sibling;
1965+
if (sibling !== null) {
1966+
ensureCorrectReturnPointer(sibling, fiber.return);
1967+
nextEffect = sibling;
1968+
return;
1969+
}
1970+
1971+
nextEffect = fiber.return;
1972+
}
1973+
}
1974+
1975+
function commitPassiveUnmountOnFiber(finishedWork: Fiber): void {
1976+
if (__DEV__) {
1977+
finishedWork.flags &= ~PassiveUnmountPendingDev;
1978+
const alternate = finishedWork.alternate;
1979+
if (alternate !== null) {
1980+
alternate.flags &= ~PassiveUnmountPendingDev;
1981+
}
1982+
}
1983+
1984+
switch (finishedWork.tag) {
1985+
case FunctionComponent:
1986+
case ForwardRef:
1987+
case SimpleMemoComponent: {
1988+
if (
1989+
enableProfilerTimer &&
1990+
enableProfilerCommitHooks &&
1991+
finishedWork.mode & ProfileMode
1992+
) {
1993+
startPassiveEffectTimer();
1994+
commitHookEffectListUnmount(HookPassive | HookHasEffect, finishedWork);
1995+
recordPassiveEffectDuration(finishedWork);
1996+
} else {
1997+
commitHookEffectListUnmount(HookPassive | HookHasEffect, finishedWork);
1998+
}
1999+
break;
2000+
}
2001+
}
2002+
}
2003+
2004+
function commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
2005+
deletedSubtreeRoot: Fiber,
2006+
) {
2007+
while (nextEffect !== null) {
2008+
const fiber = nextEffect;
2009+
const child = fiber.child;
2010+
if ((fiber.subtreeFlags & PassiveStatic) !== NoFlags && child !== null) {
2011+
ensureCorrectReturnPointer(child, fiber);
2012+
nextEffect = child;
2013+
} else {
2014+
commitPassiveUnmountEffectsInsideOfDeletedTree_complete(
2015+
deletedSubtreeRoot,
2016+
);
2017+
}
2018+
}
2019+
}
2020+
2021+
function commitPassiveUnmountEffectsInsideOfDeletedTree_complete(
2022+
deletedSubtreeRoot: Fiber,
2023+
) {
2024+
while (nextEffect !== null) {
2025+
const fiber = nextEffect;
2026+
if ((fiber.flags & PassiveStatic) !== NoFlags) {
2027+
setCurrentDebugFiberInDEV(fiber);
2028+
commitPassiveUnmountInsideDeletedTreeOnFiber(fiber);
2029+
resetCurrentDebugFiberInDEV();
2030+
}
2031+
2032+
if (fiber === deletedSubtreeRoot) {
2033+
nextEffect = null;
2034+
return;
2035+
}
2036+
2037+
const sibling = fiber.sibling;
2038+
if (sibling !== null) {
2039+
ensureCorrectReturnPointer(sibling, fiber.return);
2040+
nextEffect = sibling;
2041+
return;
2042+
}
2043+
2044+
nextEffect = fiber.return;
2045+
}
2046+
}
2047+
2048+
function commitPassiveUnmountInsideDeletedTreeOnFiber(current: Fiber): void {
2049+
switch (current.tag) {
2050+
case FunctionComponent:
2051+
case ForwardRef:
2052+
case SimpleMemoComponent: {
2053+
if (
2054+
enableProfilerTimer &&
2055+
enableProfilerCommitHooks &&
2056+
current.mode & ProfileMode
2057+
) {
2058+
startPassiveEffectTimer();
2059+
commitHookEffectListUnmount(HookPassive, current);
2060+
recordPassiveEffectDuration(current);
2061+
} else {
2062+
commitHookEffectListUnmount(HookPassive, current);
2063+
}
2064+
break;
2065+
}
2066+
}
2067+
}
2068+
19172069
let didWarnWrongReturnPointer = false;
19182070
function ensureCorrectReturnPointer(fiber, expectedReturnFiber) {
19192071
if (__DEV__) {

0 commit comments

Comments
 (0)