Skip to content

Commit

Permalink
Effects list refactor continued: passive effects traversal
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
Brian Vaughn committed Jul 24, 2020
1 parent 909b612 commit 9b91189
Show file tree
Hide file tree
Showing 5 changed files with 348 additions and 200 deletions.
72 changes: 44 additions & 28 deletions packages/react-reconciler/src/ReactFiberCommitWork.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ import {
Placement,
Snapshot,
Update,
Passive,
PassiveUnmountPendingDev,
} from './ReactSideEffectTags';
import getComponentName from 'shared/getComponentName';
import invariant from 'shared/invariant';
Expand Down Expand Up @@ -115,9 +117,8 @@ import {
captureCommitPhaseError,
resolveRetryWakeable,
markCommitTimeOfFallback,
enqueuePendingPassiveHookEffectMount,
enqueuePendingPassiveHookEffectUnmount,
enqueuePendingPassiveProfilerEffect,
schedulePassiveEffectCallback,
} from './ReactFiberWorkLoop.new';
import {
NoEffect as NoHookEffect,
Expand All @@ -130,6 +131,10 @@ import {
updateDeprecatedEventListeners,
unmountDeprecatedResponderListeners,
} from './ReactFiberDeprecatedEvents.new';
import {
NoEffect as NoSubtreeTag,
Passive as PassiveSubtreeTag,
} from './ReactSubtreeTags';

let didWarnAboutUndefinedSnapshotBeforeUpdate: Set<mixed> | null = null;
if (__DEV__) {
Expand Down Expand Up @@ -381,26 +386,6 @@ function commitHookEffectListMount(tag: number, finishedWork: Fiber) {
}
}

function schedulePassiveEffects(finishedWork: Fiber) {
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
if (lastEffect !== null) {
const firstEffect = lastEffect.next;
let effect = firstEffect;
do {
const {next, tag} = effect;
if (
(tag & HookPassive) !== NoHookEffect &&
(tag & HookHasEffect) !== NoHookEffect
) {
enqueuePendingPassiveHookEffectUnmount(finishedWork, effect);
enqueuePendingPassiveHookEffectMount(finishedWork, effect);
}
effect = next;
} while (effect !== firstEffect);
}
}

export function commitPassiveEffectDurations(
finishedRoot: FiberRoot,
finishedWork: Fiber,
Expand Down Expand Up @@ -486,7 +471,9 @@ function commitLifeCycles(
commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
}

schedulePassiveEffects(finishedWork);
if ((finishedWork.subtreeTag & PassiveSubtreeTag) !== NoSubtreeTag) {
schedulePassiveEffectCallback();
}
return;
}
case ClassComponent: {
Expand Down Expand Up @@ -892,7 +879,35 @@ function commitUnmount(
const {destroy, tag} = effect;
if (destroy !== undefined) {
if ((tag & HookPassive) !== NoHookEffect) {
enqueuePendingPassiveHookEffectUnmount(current, effect);
effect.tag |= HookHasEffect;

// subtreeTags bubble in resetChildLanes which doens't get called for unmounted subtrees.
// So in the case of unmounts, we need to bubble passive effects explicitly.
let ancestor = current.return;
while (ancestor !== null) {
ancestor.subtreeTag |= PassiveSubtreeTag;
const alternate = ancestor.alternate;
if (alternate !== null) {
alternate.subtreeTag |= PassiveSubtreeTag;
}

ancestor = ancestor.return;
}

current.effectTag |= Passive;

if (__DEV__) {
// This flag is used to avoid warning about an update to an unmounted component
// if the component has a passive unmount scheduled.
// Presumably the listener would be cleaned up by that unmount.
current.effectTag |= PassiveUnmountPendingDev;
const alternate = current.alternate;
if (alternate !== null) {
alternate.effectTag |= PassiveUnmountPendingDev;
}
}

schedulePassiveEffectCallback();
} else {
if (
enableProfilerTimer &&
Expand Down Expand Up @@ -1013,8 +1028,11 @@ function commitNestedUnmounts(
}

function detachFiberMutation(fiber: Fiber) {
// Cut off the return pointers to disconnect it from the tree. Ideally, we
// should clear the child pointer of the parent alternate to let this
// Cut off the return pointers to disconnect it from the tree.
// Note that we can't clear child or sibling pointers yet,
// because they may be required for passive effects.
// These pointers will be cleared in a separate pass.
// Ideally, we should clear the child pointer of the parent alternate to let this
// get GC:ed but we don't know which for sure which parent is the current
// one so we'll settle for GC:ing the subtree of this child. This child
// itself will be GC:ed when the parent updates the next time.
Expand All @@ -1023,7 +1041,6 @@ function detachFiberMutation(fiber: Fiber) {
// traversal in a later effect. See PR #16820. We now clear the sibling
// field after effects, see: detachFiberAfterEffects.
fiber.alternate = null;
fiber.child = null;
fiber.dependencies = null;
fiber.firstEffect = null;
fiber.lastEffect = null;
Expand All @@ -1032,7 +1049,6 @@ function detachFiberMutation(fiber: Fiber) {
fiber.pendingProps = null;
fiber.return = null;
fiber.stateNode = null;
fiber.updateQueue = null;
if (__DEV__) {
fiber._debugOwner = null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ function deleteHydratableInstance(
const childToDelete = createFiberFromHostInstanceForDeletion();
childToDelete.stateNode = instance;
childToDelete.return = returnFiber;
childToDelete.effectTag = Deletion;

const deletions = returnFiber.deletions;
if (deletions === null) {
returnFiber.deletions = [childToDelete];
Expand Down
Loading

0 comments on commit 9b91189

Please sign in to comment.