diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.new.js b/packages/react-reconciler/src/ReactFiberCompleteWork.new.js index 1c77996185319..e32b0d394f5b6 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.new.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.new.js @@ -73,6 +73,7 @@ import { NoFlags, DidCapture, Snapshot, + Visibility, MutationMask, LayoutMask, PassiveMask, @@ -1154,8 +1155,8 @@ function completeWork( // TODO: Only schedule updates if not prevDidTimeout. if (nextDidTimeout) { // If this boundary just timed out, schedule an effect to attach a - // retry listener to the promise. This flag is also used to hide the - // primary children. + // retry listener to the promise. + // TODO: Move to passive phase workInProgress.flags |= Update; } } @@ -1167,7 +1168,7 @@ function completeWork( // primary children. In mutation mode, we also need the flag to // *unhide* children that were previously hidden, so check if this // is currently timed out, too. - workInProgress.flags |= Update; + workInProgress.flags |= Update | Visibility; } } if ( @@ -1176,6 +1177,7 @@ function completeWork( workInProgress.memoizedProps.suspenseCallback != null ) { // Always notify the callback + // TODO: Move to passive phase workInProgress.flags |= Update; } bubbleProperties(workInProgress); @@ -1523,7 +1525,7 @@ function completeWork( prevIsHidden !== nextIsHidden && newProps.mode !== 'unstable-defer-without-hiding' ) { - workInProgress.flags |= Update; + workInProgress.flags |= Update | Visibility; } } diff --git a/packages/react-reconciler/src/ReactFiberFlags.js b/packages/react-reconciler/src/ReactFiberFlags.js index ed1f114e77b35..4340131c0004b 100644 --- a/packages/react-reconciler/src/ReactFiberFlags.js +++ b/packages/react-reconciler/src/ReactFiberFlags.js @@ -7,62 +7,72 @@ * @flow */ +import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags'; + export type Flags = number; // Don't change these two values. They're used by React Dev Tools. -export const NoFlags = /* */ 0b000000000000000000; -export const PerformedWork = /* */ 0b000000000000000001; +export const NoFlags = /* */ 0b0000000000000000000; +export const PerformedWork = /* */ 0b0000000000000000001; // You can change the rest (and add more). -export const Placement = /* */ 0b000000000000000010; -export const Update = /* */ 0b000000000000000100; -export const PlacementAndUpdate = /* */ 0b000000000000000110; -export const Deletion = /* */ 0b000000000000001000; -export const ContentReset = /* */ 0b000000000000010000; -export const Callback = /* */ 0b000000000000100000; -export const DidCapture = /* */ 0b000000000001000000; -export const Ref = /* */ 0b000000000010000000; -export const Snapshot = /* */ 0b000000000100000000; -export const Passive = /* */ 0b000000001000000000; -// TODO (effects) Remove this bit once the new reconciler is synced to the old. -export const PassiveUnmountPendingDev = /* */ 0b000010000000000000; -export const Hydrating = /* */ 0b000000010000000000; -export const HydratingAndUpdate = /* */ 0b000000010000000100; +export const Placement = /* */ 0b0000000000000000010; +export const Update = /* */ 0b0000000000000000100; +export const PlacementAndUpdate = /* */ 0b0000000000000000110; +export const Deletion = /* */ 0b0000000000000001000; +export const ContentReset = /* */ 0b0000000000000010000; +export const Callback = /* */ 0b0000000000000100000; +export const DidCapture = /* */ 0b0000000000001000000; +export const Ref = /* */ 0b0000000000010000000; +export const Snapshot = /* */ 0b0000000000100000000; +export const Passive = /* */ 0b0000000001000000000; +export const Hydrating = /* */ 0b0000000010000000000; +export const HydratingAndUpdate = /* */ 0b0000000010000000100; +export const Visibility = /* */ 0b0000000100000000000; // Passive & Update & Callback & Ref & Snapshot -export const LifecycleEffectMask = /* */ 0b000000001110100100; +export const LifecycleEffectMask = /* */ 0b0000000001110100100; // Union of all host effects -export const HostEffectMask = /* */ 0b000000011111111111; +export const HostEffectMask = /* */ 0b0000000111111111111; // These are not really side effects, but we still reuse this field. -export const Incomplete = /* */ 0b000000100000000000; -export const ShouldCapture = /* */ 0b000001000000000000; -export const ForceUpdateForLegacySuspense = /* */ 0b000100000000000000; +export const Incomplete = /* */ 0b0000001000000000000; +export const ShouldCapture = /* */ 0b0000010000000000000; +// TODO (effects) Remove this bit once the new reconciler is synced to the old. +export const PassiveUnmountPendingDev = /* */ 0b0000100000000000000; +export const ForceUpdateForLegacySuspense = /* */ 0b0001000000000000000; // Static tags describe aspects of a fiber that are not specific to a render, // e.g. a fiber uses a passive effect (even if there are no updates on this particular render). // This enables us to defer more work in the unmount case, // since we can defer traversing the tree during layout to look for Passive effects, // and instead rely on the static flag as a signal that there may be cleanup work. -export const PassiveStatic = /* */ 0b001000000000000000; +export const PassiveStatic = /* */ 0b0010000000000000000; // Union of side effect groupings as pertains to subtreeFlags -// TODO: Don't need to visit Placement during BeforeMutation phase -// TODO: Only need to visit Deletions during BeforeMutation phase if an element -// is focused. -export const BeforeMutationMask = /* */ 0b000000000100001010; -export const MutationMask = /* */ 0b000000010010011110; -export const LayoutMask = /* */ 0b000000000010100100; -export const PassiveMask = /* */ 0b000000001000001000; + +export const BeforeMutationMask = + Snapshot | + (enableCreateEventHandleAPI + ? // createEventHandle needs to visit deleted and hidden trees to + // fire beforeblur + // TODO: Only need to visit Deletions during BeforeMutation phase if an + // element is focused. + Deletion | Visibility + : 0); + +export const MutationMask = /* */ 0b0000000110010011110; +export const LayoutMask = /* */ 0b0000000000010100100; +export const PassiveMask = /* */ 0b0000000001000001000; // Union of tags that don't get reset on clones. // This allows certain concepts to persist without recalculting them, // e.g. whether a subtree contains passive effects or portals. -export const StaticMask = /* */ 0b001000000000000000; +export const StaticMask = /* */ 0b0010000000000000000; // These flags allow us to traverse to fibers that have effects on mount // without traversing the entire tree after every commit for // double invoking -export const MountLayoutDev = /* */ 0b010000000000000000; -export const MountPassiveDev = /* */ 0b100000000000000000; +export const MountLayoutDev = /* */ 0b0100000000000000000; +export const MountPassiveDev = /* */ 0b1000000000000000000; diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index 7eaf02ad4e3ee..33d8398fad5fe 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -134,6 +134,7 @@ import { HostEffectMask, Hydrating, HydratingAndUpdate, + Visibility, BeforeMutationMask, MutationMask, LayoutMask, @@ -2155,8 +2156,10 @@ function commitBeforeMutationEffectsImpl(fiber: Fiber) { if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) { // Check to see if the focused element was inside of a hidden (Suspense) subtree. - // TODO: Move this out of the hot path using a dedicated effect tag. if ( + // TODO: Can optimize this further with separate Hide and Show flags. We + // only care about Hide here. + (flags & Visibility) !== NoFlags && fiber.tag === SuspenseComponent && isSuspenseBoundaryBeingHidden(current, fiber) && doesFiberContain(fiber, focusedInstanceHandle)