Skip to content

Commit d96907c

Browse files
author
Brian Vaughn
committed
Effects list refactor continued: passive effects traversal
* Adds new Passive subtree tag value. * Bubbles passive flag to ancestors in the case of an unmount. * Adds recursive traversal for passive effects (mounts and unmounts). * Removes pendingPassiveHookEffectsMount and pendingPassiveHookEffectsUnmount arrays from work loop. * Re-adds sibling and child pointer detaching (temporarily removed in previous PR). * Addresses some minor TODO comments left over from previous PRs.
1 parent 356c171 commit d96907c

File tree

5 files changed

+348
-200
lines changed

5 files changed

+348
-200
lines changed

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

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ import {
6868
Placement,
6969
Snapshot,
7070
Update,
71+
Passive,
72+
PassiveUnmountPendingDev,
7173
} from './ReactSideEffectTags';
7274
import getComponentName from 'shared/getComponentName';
7375
import invariant from 'shared/invariant';
@@ -115,9 +117,8 @@ import {
115117
captureCommitPhaseError,
116118
resolveRetryWakeable,
117119
markCommitTimeOfFallback,
118-
enqueuePendingPassiveHookEffectMount,
119-
enqueuePendingPassiveHookEffectUnmount,
120120
enqueuePendingPassiveProfilerEffect,
121+
schedulePassiveEffectCallback,
121122
} from './ReactFiberWorkLoop.new';
122123
import {
123124
NoEffect as NoHookEffect,
@@ -130,6 +131,10 @@ import {
130131
updateDeprecatedEventListeners,
131132
unmountDeprecatedResponderListeners,
132133
} from './ReactFiberDeprecatedEvents.new';
134+
import {
135+
NoEffect as NoSubtreeTag,
136+
Passive as PassiveSubtreeTag,
137+
} from './ReactSubtreeTags';
133138

134139
let didWarnAboutUndefinedSnapshotBeforeUpdate: Set<mixed> | null = null;
135140
if (__DEV__) {
@@ -381,26 +386,6 @@ function commitHookEffectListMount(tag: number, finishedWork: Fiber) {
381386
}
382387
}
383388

384-
function schedulePassiveEffects(finishedWork: Fiber) {
385-
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
386-
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
387-
if (lastEffect !== null) {
388-
const firstEffect = lastEffect.next;
389-
let effect = firstEffect;
390-
do {
391-
const {next, tag} = effect;
392-
if (
393-
(tag & HookPassive) !== NoHookEffect &&
394-
(tag & HookHasEffect) !== NoHookEffect
395-
) {
396-
enqueuePendingPassiveHookEffectUnmount(finishedWork, effect);
397-
enqueuePendingPassiveHookEffectMount(finishedWork, effect);
398-
}
399-
effect = next;
400-
} while (effect !== firstEffect);
401-
}
402-
}
403-
404389
export function commitPassiveEffectDurations(
405390
finishedRoot: FiberRoot,
406391
finishedWork: Fiber,
@@ -486,7 +471,9 @@ function commitLifeCycles(
486471
commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
487472
}
488473

489-
schedulePassiveEffects(finishedWork);
474+
if ((finishedWork.subtreeTag & PassiveSubtreeTag) !== NoSubtreeTag) {
475+
schedulePassiveEffectCallback();
476+
}
490477
return;
491478
}
492479
case ClassComponent: {
@@ -892,7 +879,35 @@ function commitUnmount(
892879
const {destroy, tag} = effect;
893880
if (destroy !== undefined) {
894881
if ((tag & HookPassive) !== NoHookEffect) {
895-
enqueuePendingPassiveHookEffectUnmount(current, effect);
882+
effect.tag |= HookHasEffect;
883+
884+
// subtreeTags bubble in resetChildLanes which doens't get called for unmounted subtrees.
885+
// So in the case of unmounts, we need to bubble passive effects explicitly.
886+
let ancestor = current.return;
887+
while (ancestor !== null) {
888+
ancestor.subtreeTag |= PassiveSubtreeTag;
889+
const alternate = ancestor.alternate;
890+
if (alternate !== null) {
891+
alternate.subtreeTag |= PassiveSubtreeTag;
892+
}
893+
894+
ancestor = ancestor.return;
895+
}
896+
897+
current.effectTag |= Passive;
898+
899+
if (__DEV__) {
900+
// This flag is used to avoid warning about an update to an unmounted component
901+
// if the component has a passive unmount scheduled.
902+
// Presumably the listener would be cleaned up by that unmount.
903+
current.effectTag |= PassiveUnmountPendingDev;
904+
const alternate = current.alternate;
905+
if (alternate !== null) {
906+
alternate.effectTag |= PassiveUnmountPendingDev;
907+
}
908+
}
909+
910+
schedulePassiveEffectCallback();
896911
} else {
897912
if (
898913
enableProfilerTimer &&
@@ -1013,8 +1028,11 @@ function commitNestedUnmounts(
10131028
}
10141029

10151030
function detachFiberMutation(fiber: Fiber) {
1016-
// Cut off the return pointers to disconnect it from the tree. Ideally, we
1017-
// should clear the child pointer of the parent alternate to let this
1031+
// Cut off the return pointers to disconnect it from the tree.
1032+
// Note that we can't clear child or sibling pointers yet,
1033+
// because they may be required for passive effects.
1034+
// These pointers will be cleared in a separate pass.
1035+
// Ideally, we should clear the child pointer of the parent alternate to let this
10181036
// get GC:ed but we don't know which for sure which parent is the current
10191037
// one so we'll settle for GC:ing the subtree of this child. This child
10201038
// itself will be GC:ed when the parent updates the next time.
@@ -1023,7 +1041,6 @@ function detachFiberMutation(fiber: Fiber) {
10231041
// traversal in a later effect. See PR #16820. We now clear the sibling
10241042
// field after effects, see: detachFiberAfterEffects.
10251043
fiber.alternate = null;
1026-
fiber.child = null;
10271044
fiber.dependencies = null;
10281045
fiber.firstEffect = null;
10291046
fiber.lastEffect = null;
@@ -1032,7 +1049,6 @@ function detachFiberMutation(fiber: Fiber) {
10321049
fiber.pendingProps = null;
10331050
fiber.return = null;
10341051
fiber.stateNode = null;
1035-
fiber.updateQueue = null;
10361052
if (__DEV__) {
10371053
fiber._debugOwner = null;
10381054
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ function deleteHydratableInstance(
124124
const childToDelete = createFiberFromHostInstanceForDeletion();
125125
childToDelete.stateNode = instance;
126126
childToDelete.return = returnFiber;
127-
childToDelete.effectTag = Deletion;
127+
128128
const deletions = returnFiber.deletions;
129129
if (deletions === null) {
130130
returnFiber.deletions = [childToDelete];

0 commit comments

Comments
 (0)