From 13be8c3e565f1df708849285266b71d4f36b5497 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Fri, 4 Sep 2020 15:21:03 -0500 Subject: [PATCH] Combine Flags and SubtreeFlags types Because the `subtreeFlags` is the union of all the flags present in a subtree, we can use the same type as `flags`. One practical benefit is that we can bubble up the flags from the children with a single `|=` operator. Structurally, everything else about the effect algorithm is unchanged. --- .../react-reconciler/src/ReactFiber.new.js | 7 +- .../src/ReactFiberCommitWork.new.js | 10 +- .../src/ReactFiberCompleteWork.new.js | 3 +- .../src/ReactFiberWorkLoop.new.js | 110 +++++------------- .../src/ReactInternalTypes.js | 3 +- .../react-reconciler/src/ReactSubtreeFlags.js | 18 --- 6 files changed, 38 insertions(+), 113 deletions(-) delete mode 100644 packages/react-reconciler/src/ReactSubtreeFlags.js diff --git a/packages/react-reconciler/src/ReactFiber.new.js b/packages/react-reconciler/src/ReactFiber.new.js index 70cd847175d85..eca759ceaba6e 100644 --- a/packages/react-reconciler/src/ReactFiber.new.js +++ b/packages/react-reconciler/src/ReactFiber.new.js @@ -30,7 +30,6 @@ import { enableBlocksAPI, } from 'shared/ReactFeatureFlags'; import {NoFlags, Placement, StaticMask} from './ReactFiberFlags'; -import {NoFlags as NoSubtreeEffect} from './ReactSubtreeFlags'; import {ConcurrentRoot, BlockingRoot} from './ReactRootTags'; import { IndeterminateComponent, @@ -145,7 +144,7 @@ function FiberNode( // Effects this.flags = NoFlags; - this.subtreeFlags = NoSubtreeEffect; + this.subtreeFlags = NoFlags; this.deletions = null; this.lanes = NoLanes; @@ -284,7 +283,7 @@ export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber { workInProgress.type = current.type; // We already have an alternate. - workInProgress.subtreeFlags = NoSubtreeEffect; + workInProgress.subtreeFlags = NoFlags; workInProgress.deletions = null; if (enableProfilerTimer) { @@ -372,7 +371,7 @@ export function resetWorkInProgress(workInProgress: Fiber, renderLanes: Lanes) { workInProgress.lanes = renderLanes; workInProgress.child = null; - workInProgress.subtreeFlags = NoSubtreeEffect; + workInProgress.subtreeFlags = NoFlags; workInProgress.memoizedProps = null; workInProgress.memoizedState = null; workInProgress.updateQueue = null; diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.new.js b/packages/react-reconciler/src/ReactFiberCommitWork.new.js index 7700b6fc16862..1943b7ea2dc74 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.new.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.new.js @@ -71,6 +71,7 @@ import { Placement, Snapshot, Update, + PassiveMask, } from './ReactFiberFlags'; import getComponentName from 'shared/getComponentName'; import invariant from 'shared/invariant'; @@ -130,10 +131,6 @@ import { Passive as HookPassive, } from './ReactHookEffectTags'; import {didWarnAboutReassigningProps} from './ReactFiberBeginWork.new'; -import { - NoFlags as NoSubtreeFlags, - Passive as PassiveSubtreeFlags, -} from './ReactSubtreeFlags'; let didWarnAboutUndefinedSnapshotBeforeUpdate: Set | null = null; if (__DEV__) { @@ -595,10 +592,7 @@ function commitLifeCycles( commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork); } - if ( - (finishedWork.subtreeFlags & PassiveSubtreeFlags) !== - NoSubtreeFlags - ) { + if ((finishedWork.subtreeFlags & PassiveMask) !== NoFlags) { schedulePassiveEffectCallback(); } return; diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.new.js b/packages/react-reconciler/src/ReactFiberCompleteWork.new.js index d6dedf95e958f..3cb08b48eea5e 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.new.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.new.js @@ -67,7 +67,6 @@ import { Snapshot, MutationMask, } from './ReactFiberFlags'; -import {NoFlags as NoSubtreeFlags, Mutation} from './ReactSubtreeFlags'; import invariant from 'shared/invariant'; import { @@ -166,7 +165,7 @@ function hadNoMutationsEffects(current: null | Fiber, completedWork: Fiber) { if ((child.flags & MutationMask) !== NoFlags) { return false; } - if ((child.subtreeFlags & Mutation) !== NoSubtreeFlags) { + if ((child.subtreeFlags & MutationMask) !== NoFlags) { return false; } child = child.sibling; diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index e1dfcc0c68cf3..1e386c3309548 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -139,15 +139,8 @@ import { MutationMask, LayoutMask, PassiveMask, + StaticMask, } from './ReactFiberFlags'; -import { - NoFlags as NoSubtreeFlags, - BeforeMutation as BeforeMutationSubtreeFlags, - Mutation as MutationSubtreeFlags, - Layout as LayoutSubtreeFlags, - Passive as PassiveSubtreeFlags, - PassiveStatic as PassiveStaticSubtreeFlags, -} from './ReactSubtreeFlags'; import { NoLanePriority, SyncLanePriority, @@ -1766,7 +1759,7 @@ function completeUnitOfWork(unitOfWork: Fiber): void { if (returnFiber !== null) { // Mark the parent fiber as incomplete returnFiber.flags |= Incomplete; - returnFiber.subtreeFlags = NoSubtreeFlags; + returnFiber.subtreeFlags = NoFlags; returnFiber.deletions = null; } } @@ -1809,7 +1802,7 @@ function resetChildLanes(completedWork: Fiber) { completedWork.alternate.child === completedWork.child; let newChildLanes = NoLanes; - let subtreeFlags = NoSubtreeFlags; + let subtreeFlags = NoFlags; if (!didBailout) { // Bubble up the earliest expiration time. @@ -1827,23 +1820,7 @@ function resetChildLanes(completedWork: Fiber) { ); subtreeFlags |= child.subtreeFlags; - - const flags = child.flags; - if ((flags & BeforeMutationMask) !== NoFlags) { - subtreeFlags |= BeforeMutationSubtreeFlags; - } - if ((flags & MutationMask) !== NoFlags) { - subtreeFlags |= MutationSubtreeFlags; - } - if ((flags & LayoutMask) !== NoFlags) { - subtreeFlags |= LayoutSubtreeFlags; - } - if ((flags & PassiveMask) !== NoFlags) { - subtreeFlags |= PassiveSubtreeFlags; - } - if ((flags & PassiveStatic) !== NoFlags) { - subtreeFlags |= PassiveStaticSubtreeFlags; - } + subtreeFlags |= child.flags; // When a fiber is cloned, its actualDuration is reset to 0. This value will // only be updated if work is done on the fiber (i.e. it doesn't bailout). @@ -1880,23 +1857,7 @@ function resetChildLanes(completedWork: Fiber) { ); subtreeFlags |= child.subtreeFlags; - - const flags = child.flags; - if ((flags & BeforeMutationMask) !== NoFlags) { - subtreeFlags |= BeforeMutationSubtreeFlags; - } - if ((flags & MutationMask) !== NoFlags) { - subtreeFlags |= MutationSubtreeFlags; - } - if ((flags & LayoutMask) !== NoFlags) { - subtreeFlags |= LayoutSubtreeFlags; - } - if ((flags & PassiveMask) !== NoFlags) { - subtreeFlags |= PassiveSubtreeFlags; - } - if ((flags & PassiveStatic) !== NoFlags) { - subtreeFlags |= PassiveStaticSubtreeFlags; - } + subtreeFlags |= child.flags; child = child.sibling; } @@ -1917,13 +1878,12 @@ function resetChildLanes(completedWork: Fiber) { mergeLanes(child.lanes, child.childLanes), ); - // Preserve passive static flag even in the case of a bailout; - // otherwise a subsequent unmount may bailout before calling destroy functions. - subtreeFlags |= child.subtreeFlags & PassiveStaticSubtreeFlags; - const flags = child.flags; - if ((flags & PassiveStatic) !== NoFlags) { - subtreeFlags |= PassiveStaticSubtreeFlags; - } + // "Static" flags share the lifetime of the fiber/hook they belong to, + // so we should bubble those up even during a bailout. All the other + // flags have a lifetime only of a single render + commit, so we should + // ignore them. + subtreeFlags |= child.subtreeFlags & StaticMask; + subtreeFlags |= child.flags & StaticMask; treeBaseDuration += child.treeBaseDuration; child = child.sibling; @@ -1949,13 +1909,12 @@ function resetChildLanes(completedWork: Fiber) { mergeLanes(child.lanes, child.childLanes), ); - // Preserve passive static flag even in the case of a bailout; - // otherwise a subsequent unmount may bailout before calling destroy functions. - subtreeFlags |= child.subtreeFlags & PassiveStaticSubtreeFlags; - const flags = child.flags; - if ((flags & PassiveStatic) !== NoFlags) { - subtreeFlags |= PassiveStaticSubtreeFlags; - } + // "Static" flags share the lifetime of the fiber/hook they belong to, + // so we should bubble those up even during a bailout. All the other + // flags have a lifetime only of a single render + commit, so we should + // ignore them. + subtreeFlags |= child.subtreeFlags & StaticMask; + subtreeFlags |= child.flags & StaticMask; child = child.sibling; } @@ -2067,11 +2026,8 @@ function commitRootImpl(root, renderPriorityLevel) { // Reconsider whether this is necessary. const subtreeHasEffects = (finishedWork.subtreeFlags & - (BeforeMutationSubtreeFlags | - MutationSubtreeFlags | - LayoutSubtreeFlags | - PassiveSubtreeFlags)) !== - NoSubtreeFlags; + (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !== + NoFlags; const rootHasEffect = (finishedWork.flags & (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !== @@ -2152,7 +2108,7 @@ function commitRootImpl(root, renderPriorityLevel) { // If there are pending passive effects, schedule a callback to process them. if ( - (finishedWork.subtreeFlags & PassiveSubtreeFlags) !== NoSubtreeFlags || + (finishedWork.subtreeFlags & PassiveMask) !== NoFlags || (finishedWork.flags & PassiveMask) !== NoFlags ) { if (!rootDoesHavePassiveEffects) { @@ -2306,9 +2262,8 @@ function commitBeforeMutationEffects(firstChild: Fiber) { } if (fiber.child !== null) { - const primarySubtreeFlags = - fiber.subtreeFlags & BeforeMutationSubtreeFlags; - if (primarySubtreeFlags !== NoSubtreeFlags) { + const primarySubtreeFlags = fiber.subtreeFlags & BeforeMutationMask; + if (primarySubtreeFlags !== NoFlags) { commitBeforeMutationEffects(fiber.child); } } @@ -2402,8 +2357,8 @@ function commitMutationEffects( } if (fiber.child !== null) { - const primarySubtreeFlags = fiber.subtreeFlags & MutationSubtreeFlags; - if (primarySubtreeFlags !== NoSubtreeFlags) { + const mutationFlags = fiber.subtreeFlags & MutationMask; + if (mutationFlags !== NoFlags) { commitMutationEffects(fiber.child, root, renderPriorityLevel); } } @@ -2560,8 +2515,8 @@ function commitLayoutEffects( let fiber = firstChild; while (fiber !== null) { if (fiber.child !== null) { - const primarySubtreeFlags = fiber.subtreeFlags & LayoutSubtreeFlags; - if (primarySubtreeFlags !== NoSubtreeFlags) { + const primarySubtreeFlags = fiber.subtreeFlags & LayoutMask; + if (primarySubtreeFlags !== NoFlags) { commitLayoutEffects(fiber.child, root, committedLanes); } } @@ -2662,9 +2617,9 @@ export function enqueuePendingPassiveProfilerEffect(fiber: Fiber): void { function flushPassiveMountEffects(firstChild: Fiber): void { let fiber = firstChild; while (fiber !== null) { - const primarySubtreeFlags = fiber.subtreeFlags & PassiveSubtreeFlags; + const primarySubtreeFlags = fiber.subtreeFlags & PassiveMask; - if (fiber.child !== null && primarySubtreeFlags !== NoSubtreeFlags) { + if (fiber.child !== null && primarySubtreeFlags !== NoFlags) { flushPassiveMountEffects(fiber.child); } @@ -2698,8 +2653,8 @@ function flushPassiveUnmountEffects(firstChild: Fiber): void { // Note that this requires checking subtreeFlags of the current Fiber, // rather than the subtreeFlags/effectsTag of the first child, // since that would not cover passive effects in siblings. - const primarySubtreeFlags = fiber.subtreeFlags & PassiveSubtreeFlags; - if (primarySubtreeFlags !== NoSubtreeFlags) { + const passiveFlags = fiber.subtreeFlags & PassiveMask; + if (passiveFlags !== NoFlags) { flushPassiveUnmountEffects(child); } } @@ -2719,10 +2674,7 @@ function flushPassiveUnmountEffectsInsideOfDeletedTree( fiberToDelete: Fiber, nearestMountedAncestor: Fiber, ): void { - if ( - (fiberToDelete.subtreeFlags & PassiveStaticSubtreeFlags) !== - NoSubtreeFlags - ) { + if ((fiberToDelete.subtreeFlags & PassiveStatic) !== NoFlags) { // If any children have passive effects then traverse the subtree. // Note that this requires checking subtreeFlags of the current Fiber, // rather than the subtreeFlags/effectsTag of the first child, diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index 728aadf3f8af2..cebc8725ccb61 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -20,7 +20,6 @@ import type {SuspenseInstance} from './ReactFiberHostConfig'; import type {WorkTag} from './ReactWorkTags'; import type {TypeOfMode} from './ReactTypeOfMode'; import type {Flags} from './ReactFiberFlags'; -import type {SubtreeFlags} from './ReactSubtreeFlags'; import type {Lane, LanePriority, Lanes, LaneMap} from './ReactFiberLane'; import type {HookType} from './ReactFiberHooks.old'; import type {RootTag} from './ReactRootTags'; @@ -119,7 +118,7 @@ export type Fiber = {| // Effect flags: Flags, - subtreeFlags: SubtreeFlags, + subtreeFlags: Flags, deletions: Array | null, // Singly linked list fast path to the next fiber with side-effects. diff --git a/packages/react-reconciler/src/ReactSubtreeFlags.js b/packages/react-reconciler/src/ReactSubtreeFlags.js deleted file mode 100644 index df664c98936fb..0000000000000 --- a/packages/react-reconciler/src/ReactSubtreeFlags.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -// TODO: Move this to ReactFiberFlags so it's easier to line up the bits -export type SubtreeFlags = number; - -export const NoFlags = /* */ 0b00000; -export const BeforeMutation = /* */ 0b00001; -export const Mutation = /* */ 0b00010; -export const Layout = /* */ 0b00100; -export const Passive = /* */ 0b01000; -export const PassiveStatic = /* */ 0b10000;