import type {
Instance,
TextInstance,
SuspenseInstance,
Container,
HoistableRoot,
FormInstance,
} from './ReactFiberConfig';
import type {Fiber, FiberRoot} from './ReactInternalTypes';
import type {Lanes} from './ReactFiberLane';
import {SyncLane} from './ReactFiberLane';
import type {SuspenseState, RetryQueue} from './ReactFiberSuspenseComponent';
import type {UpdateQueue} from './ReactFiberClassUpdateQueue';
import type {FunctionComponentUpdateQueue} from './ReactFiberHooks';
import type {Wakeable} from 'shared/ReactTypes';
import {isOffscreenManual} from './ReactFiberActivityComponent';
import type {
OffscreenState,
OffscreenInstance,
OffscreenQueue,
OffscreenProps,
} from './ReactFiberActivityComponent';
import type {Cache} from './ReactFiberCacheComponent';
import type {RootState} from './ReactFiberRoot';
import type {
Transition,
TracingMarkerInstance,
TransitionAbort,
} from './ReactFiberTracingMarkerComponent';
import {
alwaysThrottleRetries,
enableCreateEventHandleAPI,
enableHiddenSubtreeInsertionEffectCleanup,
enablePersistedModeClonedFlag,
enableProfilerTimer,
enableProfilerCommitHooks,
enableSuspenseCallback,
enableScopeAPI,
enableUpdaterTracking,
enableCache,
enableTransitionTracing,
enableUseEffectEventHook,
enableLegacyHidden,
disableLegacyMode,
enableComponentPerformanceTrack,
} from 'shared/ReactFeatureFlags';
import {
FunctionComponent,
ForwardRef,
ClassComponent,
HostRoot,
HostComponent,
HostHoistable,
HostSingleton,
HostText,
HostPortal,
Profiler,
SuspenseComponent,
DehydratedFragment,
IncompleteClassComponent,
MemoComponent,
SimpleMemoComponent,
SuspenseListComponent,
ScopeComponent,
OffscreenComponent,
LegacyHiddenComponent,
CacheComponent,
TracingMarkerComponent,
} from './ReactWorkTags';
import {
NoFlags,
ContentReset,
Placement,
ChildDeletion,
Snapshot,
Update,
Callback,
Ref,
Hydrating,
Passive,
BeforeMutationMask,
MutationMask,
LayoutMask,
PassiveMask,
Visibility,
ShouldSuspendCommit,
MaySuspendCommit,
FormReset,
Cloned,
} from './ReactFiberFlags';
import {
commitStartTime,
pushNestedEffectDurations,
popNestedEffectDurations,
bubbleNestedEffectDurations,
resetComponentEffectTimers,
pushComponentEffectStart,
popComponentEffectStart,
componentEffectStartTime,
componentEffectEndTime,
componentEffectDuration,
} from './ReactProfilerTimer';
import {
logComponentRender,
logComponentEffect,
} from './ReactFiberPerformanceTrack';
import {ConcurrentMode, NoMode, ProfileMode} from './ReactTypeOfMode';
import {deferHiddenCallbacks} from './ReactFiberClassUpdateQueue';
import {
supportsMutation,
supportsPersistence,
supportsHydration,
supportsResources,
supportsSingletons,
clearSuspenseBoundary,
clearSuspenseBoundaryFromContainer,
createContainerChildSet,
clearContainer,
prepareScopeUpdate,
prepareForCommit,
beforeActiveInstanceBlur,
detachDeletedInstance,
releaseSingletonInstance,
getHoistableRoot,
acquireResource,
releaseResource,
hydrateHoistable,
mountHoistable,
unmountHoistable,
prepareToCommitHoistables,
suspendInstance,
suspendResource,
resetFormInstance,
} from './ReactFiberConfig';
import {
captureCommitPhaseError,
resolveRetryWakeable,
markCommitTimeOfFallback,
restorePendingUpdaters,
addTransitionStartCallbackToPendingTransition,
addTransitionProgressCallbackToPendingTransition,
addTransitionCompleteCallbackToPendingTransition,
addMarkerProgressCallbackToPendingTransition,
addMarkerIncompleteCallbackToPendingTransition,
addMarkerCompleteCallbackToPendingTransition,
} from './ReactFiberWorkLoop';
import {
HasEffect as HookHasEffect,
Layout as HookLayout,
Insertion as HookInsertion,
Passive as HookPassive,
} from './ReactHookEffectTags';
import {doesFiberContain} from './ReactFiberTreeReflection';
import {isDevToolsPresent, onCommitUnmount} from './ReactFiberDevToolsHook';
import {releaseCache, retainCache} from './ReactFiberCacheComponent';
import {clearTransitionsForLanes} from './ReactFiberLane';
import {
OffscreenVisible,
OffscreenDetached,
OffscreenPassiveEffectsConnected,
} from './ReactFiberActivityComponent';
import {
TransitionRoot,
TransitionTracingMarker,
} from './ReactFiberTracingMarkerComponent';
import {scheduleUpdateOnFiber} from './ReactFiberWorkLoop';
import {enqueueConcurrentRenderForLane} from './ReactFiberConcurrentUpdates';
import {
commitHookLayoutEffects,
commitHookLayoutUnmountEffects,
commitHookEffectListMount,
commitHookEffectListUnmount,
commitHookPassiveMountEffects,
commitHookPassiveUnmountEffects,
commitClassLayoutLifecycles,
commitClassDidMount,
commitClassCallbacks,
commitClassHiddenCallbacks,
commitClassSnapshot,
safelyCallComponentWillUnmount,
safelyAttachRef,
safelyDetachRef,
commitProfilerUpdate,
commitProfilerPostCommit,
commitRootCallbacks,
} from './ReactFiberCommitEffects';
import {
commitHostMount,
commitHostUpdate,
commitHostTextUpdate,
commitHostResetTextContent,
commitShowHideHostInstance,
commitShowHideHostTextInstance,
commitHostPlacement,
commitHostRootContainerChildren,
commitHostPortalContainerChildren,
commitHostHydratedContainer,
commitHostHydratedSuspense,
commitHostRemoveChildFromContainer,
commitHostRemoveChild,
commitHostSingleton,
} from './ReactFiberCommitHostEffects';
let offscreenSubtreeIsHidden: boolean = false;
let offscreenSubtreeWasHidden: boolean = false;
let needsFormReset = false;
const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
let nextEffect: Fiber | null = null;
let inProgressLanes: Lanes | null = null;
let inProgressRoot: FiberRoot | null = null;
let focusedInstanceHandle: null | Fiber = null;
let shouldFireAfterActiveInstanceBlur: boolean = false;
export function commitBeforeMutationEffects(
root: FiberRoot,
firstChild: Fiber,
): boolean {
focusedInstanceHandle = prepareForCommit(root.containerInfo);
nextEffect = firstChild;
commitBeforeMutationEffects_begin();
const shouldFire = shouldFireAfterActiveInstanceBlur;
shouldFireAfterActiveInstanceBlur = false;
focusedInstanceHandle = null;
return shouldFire;
}
function commitBeforeMutationEffects_begin() {
while (nextEffect !== null) {
const fiber = nextEffect;
if (enableCreateEventHandleAPI) {
const deletions = fiber.deletions;
if (deletions !== null) {
for (let i = 0; i < deletions.length; i++) {
const deletion = deletions[i];
commitBeforeMutationEffectsDeletion(deletion);
}
}
}
const child = fiber.child;
if (
(fiber.subtreeFlags & BeforeMutationMask) !== NoFlags &&
child !== null
) {
child.return = fiber;
nextEffect = child;
} else {
commitBeforeMutationEffects_complete();
}
}
}
function commitBeforeMutationEffects_complete() {
while (nextEffect !== null) {
const fiber = nextEffect;
commitBeforeMutationEffectsOnFiber(fiber);
const sibling = fiber.sibling;
if (sibling !== null) {
sibling.return = fiber.return;
nextEffect = sibling;
return;
}
nextEffect = fiber.return;
}
}
function commitBeforeMutationEffectsOnFiber(finishedWork: Fiber) {
const current = finishedWork.alternate;
const flags = finishedWork.flags;
if (enableCreateEventHandleAPI) {
if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {
if (
finishedWork.tag === SuspenseComponent &&
isSuspenseBoundaryBeingHidden(current, finishedWork) &&
doesFiberContain(finishedWork, focusedInstanceHandle)
) {
shouldFireAfterActiveInstanceBlur = true;
beforeActiveInstanceBlur(finishedWork);
}
}
}
switch (finishedWork.tag) {
case FunctionComponent: {
if (enableUseEffectEventHook) {
if ((flags & Update) !== NoFlags) {
const updateQueue: FunctionComponentUpdateQueue | null =
(finishedWork.updateQueue: any);
const eventPayloads =
updateQueue !== null ? updateQueue.events : null;
if (eventPayloads !== null) {
for (let ii = 0; ii < eventPayloads.length; ii++) {
const {ref, nextImpl} = eventPayloads[ii];
ref.impl = nextImpl;
}
}
}
}
break;
}
case ForwardRef:
case SimpleMemoComponent: {
break;
}
case ClassComponent: {
if ((flags & Snapshot) !== NoFlags) {
if (current !== null) {
commitClassSnapshot(finishedWork, current);
}
}
break;
}
case HostRoot: {
if ((flags & Snapshot) !== NoFlags) {
if (supportsMutation) {
const root = finishedWork.stateNode;
clearContainer(root.containerInfo);
}
}
break;
}
case HostComponent:
case HostHoistable:
case HostSingleton:
case HostText:
case HostPortal:
case IncompleteClassComponent:
break;
default: {
if ((flags & Snapshot) !== NoFlags) {
throw new Error(
'This unit of work tag should not have side-effects. This error is ' +
'likely caused by a bug in React. Please file an issue.',
);
}
}
}
}
function commitBeforeMutationEffectsDeletion(deletion: Fiber) {
if (enableCreateEventHandleAPI) {
if (doesFiberContain(deletion, ((focusedInstanceHandle: any): Fiber))) {
shouldFireAfterActiveInstanceBlur = true;
beforeActiveInstanceBlur(deletion);
}
}
}
function commitLayoutEffectOnFiber(
finishedRoot: FiberRoot,
current: Fiber | null,
finishedWork: Fiber,
committedLanes: Lanes,
): void {
const prevEffectStart = pushComponentEffectStart();
const flags = finishedWork.flags;
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
recursivelyTraverseLayoutEffects(
finishedRoot,
finishedWork,
committedLanes,
);
if (flags & Update) {
commitHookLayoutEffects(finishedWork, HookLayout | HookHasEffect);
}
break;
}
case ClassComponent: {
recursivelyTraverseLayoutEffects(
finishedRoot,
finishedWork,
committedLanes,
);
if (flags & Update) {
commitClassLayoutLifecycles(finishedWork, current);
}
if (flags & Callback) {
commitClassCallbacks(finishedWork);
}
if (flags & Ref) {
safelyAttachRef(finishedWork, finishedWork.return);
}
break;
}
case HostRoot: {
const prevEffectDuration = pushNestedEffectDurations();
recursivelyTraverseLayoutEffects(
finishedRoot,
finishedWork,
committedLanes,
);
if (flags & Callback) {
commitRootCallbacks(finishedWork);
}
if (enableProfilerTimer && enableProfilerCommitHooks) {
finishedRoot.effectDuration +=
popNestedEffectDurations(prevEffectDuration);
}
break;
}
case HostHoistable: {
if (supportsResources) {
recursivelyTraverseLayoutEffects(
finishedRoot,
finishedWork,
committedLanes,
);
if (flags & Ref) {
safelyAttachRef(finishedWork, finishedWork.return);
}
break;
}
}
case HostSingleton:
case HostComponent: {
recursivelyTraverseLayoutEffects(
finishedRoot,
finishedWork,
committedLanes,
);
if (current === null && flags & Update) {
commitHostMount(finishedWork);
}
if (flags & Ref) {
safelyAttachRef(finishedWork, finishedWork.return);
}
break;
}
case Profiler: {
if (flags & Update) {
const prevEffectDuration = pushNestedEffectDurations();
recursivelyTraverseLayoutEffects(
finishedRoot,
finishedWork,
committedLanes,
);
const profilerInstance = finishedWork.stateNode;
if (enableProfilerTimer && enableProfilerCommitHooks) {
profilerInstance.effectDuration +=
bubbleNestedEffectDurations(prevEffectDuration);
}
commitProfilerUpdate(
finishedWork,
current,
commitStartTime,
profilerInstance.effectDuration,
);
} else {
recursivelyTraverseLayoutEffects(
finishedRoot,
finishedWork,
committedLanes,
);
}
break;
}
case SuspenseComponent: {
recursivelyTraverseLayoutEffects(
finishedRoot,
finishedWork,
committedLanes,
);
if (flags & Update) {
commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);
}
break;
}
case OffscreenComponent: {
const isModernRoot =
disableLegacyMode || (finishedWork.mode & ConcurrentMode) !== NoMode;
if (isModernRoot) {
const isHidden = finishedWork.memoizedState !== null;
const newOffscreenSubtreeIsHidden =
isHidden || offscreenSubtreeIsHidden;
if (newOffscreenSubtreeIsHidden) {
} else {
const wasHidden = current !== null && current.memoizedState !== null;
const newOffscreenSubtreeWasHidden =
wasHidden || offscreenSubtreeWasHidden;
const prevOffscreenSubtreeIsHidden = offscreenSubtreeIsHidden;
const prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden;
offscreenSubtreeIsHidden = newOffscreenSubtreeIsHidden;
offscreenSubtreeWasHidden = newOffscreenSubtreeWasHidden;
if (offscreenSubtreeWasHidden && !prevOffscreenSubtreeWasHidden) {
const includeWorkInProgressEffects =
(finishedWork.subtreeFlags & LayoutMask) !== NoFlags;
recursivelyTraverseReappearLayoutEffects(
finishedRoot,
finishedWork,
includeWorkInProgressEffects,
);
} else {
recursivelyTraverseLayoutEffects(
finishedRoot,
finishedWork,
committedLanes,
);
}
offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden;
offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;
}
} else {
recursivelyTraverseLayoutEffects(
finishedRoot,
finishedWork,
committedLanes,
);
}
if (flags & Ref) {
const props: OffscreenProps = finishedWork.memoizedProps;
if (props.mode === 'manual') {
safelyAttachRef(finishedWork, finishedWork.return);
} else {
safelyDetachRef(finishedWork, finishedWork.return);
}
}
break;
}
default: {
recursivelyTraverseLayoutEffects(
finishedRoot,
finishedWork,
committedLanes,
);
break;
}
}
if (
enableProfilerTimer &&
enableProfilerCommitHooks &&
enableComponentPerformanceTrack &&
(finishedWork.mode & ProfileMode) !== NoMode &&
componentEffectStartTime >= 0 &&
componentEffectEndTime >= 0
) {
logComponentEffect(
finishedWork,
componentEffectStartTime,
componentEffectEndTime,
componentEffectDuration,
);
}
popComponentEffectStart(prevEffectStart);
}
function abortRootTransitions(
root: FiberRoot,
abort: TransitionAbort,
deletedTransitions: Set<Transition>,
deletedOffscreenInstance: OffscreenInstance | null,
isInDeletedTree: boolean,
) {
if (enableTransitionTracing) {
const rootTransitions = root.incompleteTransitions;
deletedTransitions.forEach(transition => {
if (rootTransitions.has(transition)) {
const transitionInstance: TracingMarkerInstance = (rootTransitions.get(
transition,
): any);
if (transitionInstance.aborts === null) {
transitionInstance.aborts = [];
}
transitionInstance.aborts.push(abort);
if (deletedOffscreenInstance !== null) {
if (
transitionInstance.pendingBoundaries !== null &&
transitionInstance.pendingBoundaries.has(deletedOffscreenInstance)
) {
transitionInstance.pendingBoundaries.delete(
deletedOffscreenInstance,
);
}
}
}
});
}
}
function abortTracingMarkerTransitions(
abortedFiber: Fiber,
abort: TransitionAbort,
deletedTransitions: Set<Transition>,
deletedOffscreenInstance: OffscreenInstance | null,
isInDeletedTree: boolean,
) {
if (enableTransitionTracing) {
const markerInstance: TracingMarkerInstance = abortedFiber.stateNode;
const markerTransitions = markerInstance.transitions;
const pendingBoundaries = markerInstance.pendingBoundaries;
if (markerTransitions !== null) {
deletedTransitions.forEach(transition => {
if (
abortedFiber !== null &&
markerTransitions.has(transition) &&
(markerInstance.aborts === null ||
!markerInstance.aborts.includes(abort))
) {
if (markerInstance.transitions !== null) {
if (markerInstance.aborts === null) {
markerInstance.aborts = [abort];
addMarkerIncompleteCallbackToPendingTransition(
abortedFiber.memoizedProps.name,
markerInstance.transitions,
markerInstance.aborts,
);
} else {
markerInstance.aborts.push(abort);
}
if (
deletedOffscreenInstance !== null &&
!isInDeletedTree &&
pendingBoundaries !== null &&
pendingBoundaries.has(deletedOffscreenInstance)
) {
pendingBoundaries.delete(deletedOffscreenInstance);
addMarkerProgressCallbackToPendingTransition(
abortedFiber.memoizedProps.name,
deletedTransitions,
pendingBoundaries,
);
}
}
}
});
}
}
}
function abortParentMarkerTransitionsForDeletedFiber(
abortedFiber: Fiber,
abort: TransitionAbort,
deletedTransitions: Set<Transition>,
deletedOffscreenInstance: OffscreenInstance | null,
isInDeletedTree: boolean,
) {
if (enableTransitionTracing) {
let fiber: null | Fiber = abortedFiber;
while (fiber !== null) {
switch (fiber.tag) {
case TracingMarkerComponent:
abortTracingMarkerTransitions(
fiber,
abort,
deletedTransitions,
deletedOffscreenInstance,
isInDeletedTree,
);
break;
case HostRoot:
const root = fiber.stateNode;
abortRootTransitions(
root,
abort,
deletedTransitions,
deletedOffscreenInstance,
isInDeletedTree,
);
break;
default:
break;
}
fiber = fiber.return;
}
}
}
function commitTransitionProgress(offscreenFiber: Fiber) {
if (enableTransitionTracing) {
const offscreenInstance: OffscreenInstance = offscreenFiber.stateNode;
let prevState: SuspenseState | null = null;
const previousFiber = offscreenFiber.alternate;
if (previousFiber !== null && previousFiber.memoizedState !== null) {
prevState = previousFiber.memoizedState;
}
const nextState: SuspenseState | null = offscreenFiber.memoizedState;
const wasHidden = prevState !== null;
const isHidden = nextState !== null;
const pendingMarkers = offscreenInstance._pendingMarkers;
let name = null;
const parent = offscreenFiber.return;
if (
parent !== null &&
parent.tag === SuspenseComponent &&
parent.memoizedProps.unstable_name
) {
name = parent.memoizedProps.unstable_name;
}
if (!wasHidden && isHidden) {
if (pendingMarkers !== null) {
pendingMarkers.forEach(markerInstance => {
const pendingBoundaries = markerInstance.pendingBoundaries;
const transitions = markerInstance.transitions;
const markerName = markerInstance.name;
if (
pendingBoundaries !== null &&
!pendingBoundaries.has(offscreenInstance)
) {
pendingBoundaries.set(offscreenInstance, {
name,
});
if (transitions !== null) {
if (
markerInstance.tag === TransitionTracingMarker &&
markerName !== null
) {
addMarkerProgressCallbackToPendingTransition(
markerName,
transitions,
pendingBoundaries,
);
} else if (markerInstance.tag === TransitionRoot) {
transitions.forEach(transition => {
addTransitionProgressCallbackToPendingTransition(
transition,
pendingBoundaries,
);
});
}
}
}
});
}
} else if (wasHidden && !isHidden) {
if (pendingMarkers !== null) {
pendingMarkers.forEach(markerInstance => {
const pendingBoundaries = markerInstance.pendingBoundaries;
const transitions = markerInstance.transitions;
const markerName = markerInstance.name;
if (
pendingBoundaries !== null &&
pendingBoundaries.has(offscreenInstance)
) {
pendingBoundaries.delete(offscreenInstance);
if (transitions !== null) {
if (
markerInstance.tag === TransitionTracingMarker &&
markerName !== null
) {
addMarkerProgressCallbackToPendingTransition(
markerName,
transitions,
pendingBoundaries,
);
if (pendingBoundaries.size === 0) {
if (markerInstance.aborts === null) {
addMarkerCompleteCallbackToPendingTransition(
markerName,
transitions,
);
}
markerInstance.transitions = null;
markerInstance.pendingBoundaries = null;
markerInstance.aborts = null;
}
} else if (markerInstance.tag === TransitionRoot) {
transitions.forEach(transition => {
addTransitionProgressCallbackToPendingTransition(
transition,
pendingBoundaries,
);
});
}
}
}
});
}
}
}
}
function hideOrUnhideAllChildren(finishedWork: Fiber, isHidden: boolean) {
let hostSubtreeRoot = null;
if (supportsMutation) {
let node: Fiber = finishedWork;
while (true) {
if (
node.tag === HostComponent ||
(supportsResources ? node.tag === HostHoistable : false) ||
(supportsSingletons ? node.tag === HostSingleton : false)
) {
if (hostSubtreeRoot === null) {
hostSubtreeRoot = node;
commitShowHideHostInstance(node, isHidden);
}
} else if (node.tag === HostText) {
if (hostSubtreeRoot === null) {
commitShowHideHostTextInstance(node, isHidden);
}
} else if (
(node.tag === OffscreenComponent ||
node.tag === LegacyHiddenComponent) &&
(node.memoizedState: OffscreenState) !== null &&
node !== finishedWork
) {
} else if (node.child !== null) {
node.child.return = node;
node = node.child;
continue;
}
if (node === finishedWork) {
return;
}
while (node.sibling === null) {
if (node.return === null || node.return === finishedWork) {
return;
}
if (hostSubtreeRoot === node) {
hostSubtreeRoot = null;
}
node = node.return;
}
if (hostSubtreeRoot === node) {
hostSubtreeRoot = null;
}
node.sibling.return = node.return;
node = node.sibling;
}
}
}
function detachFiberMutation(fiber: Fiber) {
const alternate = fiber.alternate;
if (alternate !== null) {
alternate.return = null;
}
fiber.return = null;
}
function detachFiberAfterEffects(fiber: Fiber) {
const alternate = fiber.alternate;
if (alternate !== null) {
fiber.alternate = null;
detachFiberAfterEffects(alternate);
}
fiber.child = null;
fiber.deletions = null;
fiber.sibling = null;
if (fiber.tag === HostComponent) {
const hostInstance: Instance = fiber.stateNode;
if (hostInstance !== null) {
detachDeletedInstance(hostInstance);
}
}
fiber.stateNode = null;
if (__DEV__) {
fiber._debugOwner = null;
}
fiber.return = null;
fiber.dependencies = null;
fiber.memoizedProps = null;
fiber.memoizedState = null;
fiber.pendingProps = null;
fiber.stateNode = null;
fiber.updateQueue = null;
}
let hostParent: Instance | Container | null = null;
let hostParentIsContainer: boolean = false;
function commitDeletionEffects(
root: FiberRoot,
returnFiber: Fiber,
deletedFiber: Fiber,
) {
if (supportsMutation) {
let parent: null | Fiber = returnFiber;
findParent: while (parent !== null) {
switch (parent.tag) {
case HostSingleton:
case HostComponent: {
hostParent = parent.stateNode;
hostParentIsContainer = false;
break findParent;
}
case HostRoot: {
hostParent = parent.stateNode.containerInfo;
hostParentIsContainer = true;
break findParent;
}
case HostPortal: {
hostParent = parent.stateNode.containerInfo;
hostParentIsContainer = true;
break findParent;
}
}
parent = parent.return;
}
if (hostParent === null) {
throw new Error(
'Expected to find a host parent. This error is likely caused by ' +
'a bug in React. Please file an issue.',
);
}
commitDeletionEffectsOnFiber(root, returnFiber, deletedFiber);
hostParent = null;
hostParentIsContainer = false;
} else {
commitDeletionEffectsOnFiber(root, returnFiber, deletedFiber);
}
detachFiberMutation(deletedFiber);
}
function recursivelyTraverseDeletionEffects(
finishedRoot: FiberRoot,
nearestMountedAncestor: Fiber,
parent: Fiber,
) {
let child = parent.child;
while (child !== null) {
commitDeletionEffectsOnFiber(finishedRoot, nearestMountedAncestor, child);
child = child.sibling;
}
}
function commitDeletionEffectsOnFiber(
finishedRoot: FiberRoot,
nearestMountedAncestor: Fiber,
deletedFiber: Fiber,
) {
onCommitUnmount(deletedFiber);
switch (deletedFiber.tag) {
case HostHoistable: {
if (supportsResources) {
if (!offscreenSubtreeWasHidden) {
safelyDetachRef(deletedFiber, nearestMountedAncestor);
}
recursivelyTraverseDeletionEffects(
finishedRoot,
nearestMountedAncestor,
deletedFiber,
);
if (deletedFiber.memoizedState) {
releaseResource(deletedFiber.memoizedState);
} else if (deletedFiber.stateNode) {
unmountHoistable(deletedFiber.stateNode);
}
return;
}
}
case HostSingleton: {
if (supportsSingletons) {
if (!offscreenSubtreeWasHidden) {
safelyDetachRef(deletedFiber, nearestMountedAncestor);
}
const prevHostParent = hostParent;
const prevHostParentIsContainer = hostParentIsContainer;
hostParent = deletedFiber.stateNode;
recursivelyTraverseDeletionEffects(
finishedRoot,
nearestMountedAncestor,
deletedFiber,
);
releaseSingletonInstance(deletedFiber.stateNode);
hostParent = prevHostParent;
hostParentIsContainer = prevHostParentIsContainer;
return;
}
}
case HostComponent: {
if (!offscreenSubtreeWasHidden) {
safelyDetachRef(deletedFiber, nearestMountedAncestor);
}
}
case HostText: {
if (supportsMutation) {
const prevHostParent = hostParent;
const prevHostParentIsContainer = hostParentIsContainer;
hostParent = null;
recursivelyTraverseDeletionEffects(
finishedRoot,
nearestMountedAncestor,
deletedFiber,
);
hostParent = prevHostParent;
hostParentIsContainer = prevHostParentIsContainer;
if (hostParent !== null) {
if (hostParentIsContainer) {
commitHostRemoveChildFromContainer(
deletedFiber,
nearestMountedAncestor,
((hostParent: any): Container),
(deletedFiber.stateNode: Instance | TextInstance),
);
} else {
commitHostRemoveChild(
deletedFiber,
nearestMountedAncestor,
((hostParent: any): Instance),
(deletedFiber.stateNode: Instance | TextInstance),
);
}
}
} else {
recursivelyTraverseDeletionEffects(
finishedRoot,
nearestMountedAncestor,
deletedFiber,
);
}
return;
}
case DehydratedFragment: {
if (enableSuspenseCallback) {
const hydrationCallbacks = finishedRoot.hydrationCallbacks;
if (hydrationCallbacks !== null) {
try {
const onDeleted = hydrationCallbacks.onDeleted;
if (onDeleted) {
onDeleted((deletedFiber.stateNode: SuspenseInstance));
}
} catch (error) {
captureCommitPhaseError(
deletedFiber,
nearestMountedAncestor,
error,
);
}
}
}
if (supportsMutation) {
if (hostParent !== null) {
if (hostParentIsContainer) {
clearSuspenseBoundaryFromContainer(
((hostParent: any): Container),
(deletedFiber.stateNode: SuspenseInstance),
);
} else {
clearSuspenseBoundary(
((hostParent: any): Instance),
(deletedFiber.stateNode: SuspenseInstance),
);
}
}
}
return;
}
case HostPortal: {
if (supportsMutation) {
const prevHostParent = hostParent;
const prevHostParentIsContainer = hostParentIsContainer;
hostParent = deletedFiber.stateNode.containerInfo;
hostParentIsContainer = true;
recursivelyTraverseDeletionEffects(
finishedRoot,
nearestMountedAncestor,
deletedFiber,
);
hostParent = prevHostParent;
hostParentIsContainer = prevHostParentIsContainer;
} else {
if (supportsPersistence) {
commitHostPortalContainerChildren(
deletedFiber.stateNode,
deletedFiber,
createContainerChildSet(),
);
}
recursivelyTraverseDeletionEffects(
finishedRoot,
nearestMountedAncestor,
deletedFiber,
);
}
return;
}
case FunctionComponent:
case ForwardRef:
case MemoComponent:
case SimpleMemoComponent: {
if (
enableHiddenSubtreeInsertionEffectCleanup ||
!offscreenSubtreeWasHidden
) {
commitHookEffectListUnmount(
HookInsertion,
deletedFiber,
nearestMountedAncestor,
);
}
if (!offscreenSubtreeWasHidden) {
commitHookLayoutUnmountEffects(
deletedFiber,
nearestMountedAncestor,
HookLayout,
);
}
recursivelyTraverseDeletionEffects(
finishedRoot,
nearestMountedAncestor,
deletedFiber,
);
return;
}
case ClassComponent: {
if (!offscreenSubtreeWasHidden) {
safelyDetachRef(deletedFiber, nearestMountedAncestor);
const instance = deletedFiber.stateNode;
if (typeof instance.componentWillUnmount === 'function') {
safelyCallComponentWillUnmount(
deletedFiber,
nearestMountedAncestor,
instance,
);
}
}
recursivelyTraverseDeletionEffects(
finishedRoot,
nearestMountedAncestor,
deletedFiber,
);
return;
}
case ScopeComponent: {
if (enableScopeAPI) {
safelyDetachRef(deletedFiber, nearestMountedAncestor);
}
recursivelyTraverseDeletionEffects(
finishedRoot,
nearestMountedAncestor,
deletedFiber,
);
return;
}
case OffscreenComponent: {
safelyDetachRef(deletedFiber, nearestMountedAncestor);
if (disableLegacyMode || deletedFiber.mode & ConcurrentMode) {
const prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden;
offscreenSubtreeWasHidden =
prevOffscreenSubtreeWasHidden || deletedFiber.memoizedState !== null;
recursivelyTraverseDeletionEffects(
finishedRoot,
nearestMountedAncestor,
deletedFiber,
);
offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;
} else {
recursivelyTraverseDeletionEffects(
finishedRoot,
nearestMountedAncestor,
deletedFiber,
);
}
break;
}
default: {
recursivelyTraverseDeletionEffects(
finishedRoot,
nearestMountedAncestor,
deletedFiber,
);
return;
}
}
}
function commitSuspenseCallback(finishedWork: Fiber) {
const newState: SuspenseState | null = finishedWork.memoizedState;
if (enableSuspenseCallback && newState !== null) {
const suspenseCallback = finishedWork.memoizedProps.suspenseCallback;
if (typeof suspenseCallback === 'function') {
const retryQueue: RetryQueue | null = (finishedWork.updateQueue: any);
if (retryQueue !== null) {
suspenseCallback(new Set(retryQueue));
}
} else if (__DEV__) {
if (suspenseCallback !== undefined) {
console.error('Unexpected type for suspenseCallback.');
}
}
}
}
function commitSuspenseHydrationCallbacks(
finishedRoot: FiberRoot,
finishedWork: Fiber,
) {
if (!supportsHydration) {
return;
}
const newState: SuspenseState | null = finishedWork.memoizedState;
if (newState === null) {
const current = finishedWork.alternate;
if (current !== null) {
const prevState: SuspenseState | null = current.memoizedState;
if (prevState !== null) {
const suspenseInstance = prevState.dehydrated;
if (suspenseInstance !== null) {
commitHostHydratedSuspense(suspenseInstance, finishedWork);
if (enableSuspenseCallback) {
try {
const hydrationCallbacks = finishedRoot.hydrationCallbacks;
if (hydrationCallbacks !== null) {
const onHydrated = hydrationCallbacks.onHydrated;
if (onHydrated) {
onHydrated(suspenseInstance);
}
}
} catch (error) {
captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
}
}
}
}
}
}
function getRetryCache(finishedWork: Fiber) {
switch (finishedWork.tag) {
case SuspenseComponent:
case SuspenseListComponent: {
let retryCache = finishedWork.stateNode;
if (retryCache === null) {
retryCache = finishedWork.stateNode = new PossiblyWeakSet();
}
return retryCache;
}
case OffscreenComponent: {
const instance: OffscreenInstance = finishedWork.stateNode;
let retryCache: null | Set<Wakeable> | WeakSet<Wakeable> =
instance._retryCache;
if (retryCache === null) {
retryCache = instance._retryCache = new PossiblyWeakSet();
}
return retryCache;
}
default: {
throw new Error(
`Unexpected Suspense handler tag (${finishedWork.tag}). This is a ` +
'bug in React.',
);
}
}
}
export function detachOffscreenInstance(instance: OffscreenInstance): void {
const fiber = instance._current;
if (fiber === null) {
throw new Error(
'Calling Offscreen.detach before instance handle has been set.',
);
}
if ((instance._pendingVisibility & OffscreenDetached) !== NoFlags) {
return;
}
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
if (root !== null) {
instance._pendingVisibility |= OffscreenDetached;
scheduleUpdateOnFiber(root, fiber, SyncLane);
}
}
export function attachOffscreenInstance(instance: OffscreenInstance): void {
const fiber = instance._current;
if (fiber === null) {
throw new Error(
'Calling Offscreen.detach before instance handle has been set.',
);
}
if ((instance._pendingVisibility & OffscreenDetached) === NoFlags) {
return;
}
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
if (root !== null) {
instance._pendingVisibility &= ~OffscreenDetached;
scheduleUpdateOnFiber(root, fiber, SyncLane);
}
}
function attachSuspenseRetryListeners(
finishedWork: Fiber,
wakeables: RetryQueue,
) {
const retryCache = getRetryCache(finishedWork);
wakeables.forEach(wakeable => {
const retry = resolveRetryWakeable.bind(null, finishedWork, wakeable);
if (!retryCache.has(wakeable)) {
retryCache.add(wakeable);
if (enableUpdaterTracking) {
if (isDevToolsPresent) {
if (inProgressLanes !== null && inProgressRoot !== null) {
restorePendingUpdaters(inProgressRoot, inProgressLanes);
} else {
throw Error(
'Expected finished root and lanes to be set. This is a bug in React.',
);
}
}
}
wakeable.then(retry, retry);
}
});
}
function isSuspenseBoundaryBeingHidden(
current: Fiber | null,
finishedWork: Fiber,
): boolean {
if (current !== null) {
const oldState: SuspenseState | null = current.memoizedState;
if (oldState === null || oldState.dehydrated !== null) {
const newState: SuspenseState | null = finishedWork.memoizedState;
return newState !== null && newState.dehydrated === null;
}
}
return false;
}
export function commitMutationEffects(
root: FiberRoot,
finishedWork: Fiber,
committedLanes: Lanes,
) {
inProgressLanes = committedLanes;
inProgressRoot = root;
resetComponentEffectTimers();
commitMutationEffectsOnFiber(finishedWork, root, committedLanes);
inProgressLanes = null;
inProgressRoot = null;
}
function recursivelyTraverseMutationEffects(
root: FiberRoot,
parentFiber: Fiber,
lanes: Lanes,
) {
const deletions = parentFiber.deletions;
if (deletions !== null) {
for (let i = 0; i < deletions.length; i++) {
const childToDelete = deletions[i];
commitDeletionEffects(root, parentFiber, childToDelete);
}
}
if (
parentFiber.subtreeFlags &
(enablePersistedModeClonedFlag ? MutationMask | Cloned : MutationMask)
) {
let child = parentFiber.child;
while (child !== null) {
commitMutationEffectsOnFiber(child, root, lanes);
child = child.sibling;
}
}
}
let currentHoistableRoot: HoistableRoot | null = null;
function commitMutationEffectsOnFiber(
finishedWork: Fiber,
root: FiberRoot,
lanes: Lanes,
) {
const prevEffectStart = pushComponentEffectStart();
const current = finishedWork.alternate;
const flags = finishedWork.flags;
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case MemoComponent:
case SimpleMemoComponent: {
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
commitReconciliationEffects(finishedWork);
if (flags & Update) {
commitHookEffectListUnmount(
HookInsertion | HookHasEffect,
finishedWork,
finishedWork.return,
);
commitHookEffectListMount(HookInsertion | HookHasEffect, finishedWork);
commitHookLayoutUnmountEffects(
finishedWork,
finishedWork.return,
HookLayout | HookHasEffect,
);
}
break;
}
case ClassComponent: {
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
commitReconciliationEffects(finishedWork);
if (flags & Ref) {
if (current !== null) {
safelyDetachRef(current, current.return);
}
}
if (flags & Callback && offscreenSubtreeIsHidden) {
const updateQueue: UpdateQueue<mixed> | null =
(finishedWork.updateQueue: any);
if (updateQueue !== null) {
deferHiddenCallbacks(updateQueue);
}
}
break;
}
case HostHoistable: {
if (supportsResources) {
const hoistableRoot: HoistableRoot = (currentHoistableRoot: any);
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
commitReconciliationEffects(finishedWork);
if (flags & Ref) {
if (current !== null) {
safelyDetachRef(current, current.return);
}
}
if (flags & Update) {
const currentResource =
current !== null ? current.memoizedState : null;
const newResource = finishedWork.memoizedState;
if (current === null) {
if (newResource === null) {
if (finishedWork.stateNode === null) {
finishedWork.stateNode = hydrateHoistable(
hoistableRoot,
finishedWork.type,
finishedWork.memoizedProps,
finishedWork,
);
} else {
mountHoistable(
hoistableRoot,
finishedWork.type,
finishedWork.stateNode,
);
}
} else {
finishedWork.stateNode = acquireResource(
hoistableRoot,
newResource,
finishedWork.memoizedProps,
);
}
} else if (currentResource !== newResource) {
if (currentResource === null) {
if (current.stateNode !== null) {
unmountHoistable(current.stateNode);
}
} else {
releaseResource(currentResource);
}
if (newResource === null) {
mountHoistable(
hoistableRoot,
finishedWork.type,
finishedWork.stateNode,
);
} else {
acquireResource(
hoistableRoot,
newResource,
finishedWork.memoizedProps,
);
}
} else if (newResource === null && finishedWork.stateNode !== null) {
commitHostUpdate(
finishedWork,
finishedWork.memoizedProps,
current.memoizedProps,
);
}
}
break;
}
}
case HostSingleton: {
if (supportsSingletons) {
if (flags & Update) {
const previousWork = finishedWork.alternate;
if (previousWork === null) {
commitHostSingleton(finishedWork);
}
}
}
}
case HostComponent: {
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
commitReconciliationEffects(finishedWork);
if (flags & Ref) {
if (current !== null) {
safelyDetachRef(current, current.return);
}
}
if (supportsMutation) {
if (finishedWork.flags & ContentReset) {
commitHostResetTextContent(finishedWork);
}
if (flags & Update) {
const instance: Instance = finishedWork.stateNode;
if (instance != null) {
const newProps = finishedWork.memoizedProps;
const oldProps =
current !== null ? current.memoizedProps : newProps;
commitHostUpdate(finishedWork, newProps, oldProps);
}
}
if (flags & FormReset) {
needsFormReset = true;
if (__DEV__) {
if (finishedWork.type !== 'form') {
console.error(
'Unexpected host component type. Expected a form. This is a ' +
'bug in React.',
);
}
}
}
}
break;
}
case HostText: {
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
commitReconciliationEffects(finishedWork);
if (flags & Update) {
if (supportsMutation) {
if (finishedWork.stateNode === null) {
throw new Error(
'This should have a text node initialized. This error is likely ' +
'caused by a bug in React. Please file an issue.',
);
}
const newText: string = finishedWork.memoizedProps;
const oldText: string =
current !== null ? current.memoizedProps : newText;
commitHostTextUpdate(finishedWork, newText, oldText);
}
}
break;
}
case HostRoot: {
const prevEffectDuration = pushNestedEffectDurations();
if (supportsResources) {
prepareToCommitHoistables();
const previousHoistableRoot = currentHoistableRoot;
currentHoistableRoot = getHoistableRoot(root.containerInfo);
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
currentHoistableRoot = previousHoistableRoot;
commitReconciliationEffects(finishedWork);
} else {
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
commitReconciliationEffects(finishedWork);
}
if (flags & Update) {
if (supportsMutation && supportsHydration) {
if (current !== null) {
const prevRootState: RootState = current.memoizedState;
if (prevRootState.isDehydrated) {
commitHostHydratedContainer(root, finishedWork);
}
}
}
if (supportsPersistence) {
commitHostRootContainerChildren(root, finishedWork);
}
}
if (needsFormReset) {
needsFormReset = false;
recursivelyResetForms(finishedWork);
}
if (enableProfilerTimer && enableProfilerCommitHooks) {
root.effectDuration += popNestedEffectDurations(prevEffectDuration);
}
break;
}
case HostPortal: {
if (supportsResources) {
const previousHoistableRoot = currentHoistableRoot;
currentHoistableRoot = getHoistableRoot(
finishedWork.stateNode.containerInfo,
);
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
commitReconciliationEffects(finishedWork);
currentHoistableRoot = previousHoistableRoot;
} else {
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
commitReconciliationEffects(finishedWork);
}
if (flags & Update) {
if (supportsPersistence) {
commitHostPortalContainerChildren(
finishedWork.stateNode,
finishedWork,
finishedWork.stateNode.pendingChildren,
);
}
}
break;
}
case Profiler: {
const prevEffectDuration = pushNestedEffectDurations();
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
commitReconciliationEffects(finishedWork);
if (enableProfilerTimer && enableProfilerCommitHooks) {
const profilerInstance = finishedWork.stateNode;
profilerInstance.effectDuration +=
bubbleNestedEffectDurations(prevEffectDuration);
}
break;
}
case SuspenseComponent: {
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
commitReconciliationEffects(finishedWork);
const offscreenFiber: Fiber = (finishedWork.child: any);
if (offscreenFiber.flags & Visibility) {
const isShowingFallback =
(finishedWork.memoizedState: SuspenseState | null) !== null;
const wasShowingFallback =
current !== null &&
(current.memoizedState: SuspenseState | null) !== null;
if (alwaysThrottleRetries) {
if (isShowingFallback !== wasShowingFallback) {
markCommitTimeOfFallback();
}
} else {
if (isShowingFallback && !wasShowingFallback) {
markCommitTimeOfFallback();
}
}
}
if (flags & Update) {
try {
commitSuspenseCallback(finishedWork);
} catch (error) {
captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
const retryQueue: RetryQueue | null = (finishedWork.updateQueue: any);
if (retryQueue !== null) {
finishedWork.updateQueue = null;
attachSuspenseRetryListeners(finishedWork, retryQueue);
}
}
break;
}
case OffscreenComponent: {
if (flags & Ref) {
if (current !== null) {
safelyDetachRef(current, current.return);
}
}
const newState: OffscreenState | null = finishedWork.memoizedState;
const isHidden = newState !== null;
const wasHidden = current !== null && current.memoizedState !== null;
if (disableLegacyMode || finishedWork.mode & ConcurrentMode) {
const prevOffscreenSubtreeIsHidden = offscreenSubtreeIsHidden;
const prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden;
offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden || isHidden;
offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden || wasHidden;
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;
offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden;
} else {
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
}
commitReconciliationEffects(finishedWork);
const offscreenInstance: OffscreenInstance = finishedWork.stateNode;
offscreenInstance._current = finishedWork;
offscreenInstance._visibility &= ~OffscreenDetached;
offscreenInstance._visibility |=
offscreenInstance._pendingVisibility & OffscreenDetached;
if (flags & Visibility) {
if (isHidden) {
offscreenInstance._visibility &= ~OffscreenVisible;
} else {
offscreenInstance._visibility |= OffscreenVisible;
}
if (isHidden) {
const isUpdate = current !== null;
const wasHiddenByAncestorOffscreen =
offscreenSubtreeIsHidden || offscreenSubtreeWasHidden;
if (isUpdate && !wasHidden && !wasHiddenByAncestorOffscreen) {
if (
disableLegacyMode ||
(finishedWork.mode & ConcurrentMode) !== NoMode
) {
recursivelyTraverseDisappearLayoutEffects(finishedWork);
}
}
} else {
if (wasHidden) {
}
}
if (supportsMutation && !isOffscreenManual(finishedWork)) {
hideOrUnhideAllChildren(finishedWork, isHidden);
}
}
if (flags & Update) {
const offscreenQueue: OffscreenQueue | null =
(finishedWork.updateQueue: any);
if (offscreenQueue !== null) {
const retryQueue = offscreenQueue.retryQueue;
if (retryQueue !== null) {
offscreenQueue.retryQueue = null;
attachSuspenseRetryListeners(finishedWork, retryQueue);
}
}
}
break;
}
case SuspenseListComponent: {
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
commitReconciliationEffects(finishedWork);
if (flags & Update) {
const retryQueue: Set<Wakeable> | null =
(finishedWork.updateQueue: any);
if (retryQueue !== null) {
finishedWork.updateQueue = null;
attachSuspenseRetryListeners(finishedWork, retryQueue);
}
}
break;
}
case ScopeComponent: {
if (enableScopeAPI) {
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
commitReconciliationEffects(finishedWork);
if (flags & Ref) {
if (current !== null) {
safelyDetachRef(finishedWork, finishedWork.return);
}
safelyAttachRef(finishedWork, finishedWork.return);
}
if (flags & Update) {
const scopeInstance = finishedWork.stateNode;
prepareScopeUpdate(scopeInstance, finishedWork);
}
}
break;
}
default: {
recursivelyTraverseMutationEffects(root, finishedWork, lanes);
commitReconciliationEffects(finishedWork);
break;
}
}
if (
enableProfilerTimer &&
enableProfilerCommitHooks &&
enableComponentPerformanceTrack &&
(finishedWork.mode & ProfileMode) !== NoMode &&
componentEffectStartTime >= 0 &&
componentEffectEndTime >= 0
) {
logComponentEffect(
finishedWork,
componentEffectStartTime,
componentEffectEndTime,
componentEffectDuration,
);
}
popComponentEffectStart(prevEffectStart);
}
function commitReconciliationEffects(finishedWork: Fiber) {
const flags = finishedWork.flags;
if (flags & Placement) {
commitHostPlacement(finishedWork);
finishedWork.flags &= ~Placement;
}
if (flags & Hydrating) {
finishedWork.flags &= ~Hydrating;
}
}
function recursivelyResetForms(parentFiber: Fiber) {
if (parentFiber.subtreeFlags & FormReset) {
let child = parentFiber.child;
while (child !== null) {
resetFormOnFiber(child);
child = child.sibling;
}
}
}
function resetFormOnFiber(fiber: Fiber) {
recursivelyResetForms(fiber);
if (fiber.tag === HostComponent && fiber.flags & FormReset) {
const formInstance: FormInstance = fiber.stateNode;
resetFormInstance(formInstance);
}
}
export function commitLayoutEffects(
finishedWork: Fiber,
root: FiberRoot,
committedLanes: Lanes,
): void {
inProgressLanes = committedLanes;
inProgressRoot = root;
resetComponentEffectTimers();
const current = finishedWork.alternate;
commitLayoutEffectOnFiber(root, current, finishedWork, committedLanes);
inProgressLanes = null;
inProgressRoot = null;
}
function recursivelyTraverseLayoutEffects(
root: FiberRoot,
parentFiber: Fiber,
lanes: Lanes,
) {
if (parentFiber.subtreeFlags & LayoutMask) {
let child = parentFiber.child;
while (child !== null) {
const current = child.alternate;
commitLayoutEffectOnFiber(root, current, child, lanes);
child = child.sibling;
}
}
}
export function disappearLayoutEffects(finishedWork: Fiber) {
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case MemoComponent:
case SimpleMemoComponent: {
commitHookLayoutUnmountEffects(
finishedWork,
finishedWork.return,
HookLayout,
);
recursivelyTraverseDisappearLayoutEffects(finishedWork);
break;
}
case ClassComponent: {
safelyDetachRef(finishedWork, finishedWork.return);
const instance = finishedWork.stateNode;
if (typeof instance.componentWillUnmount === 'function') {
safelyCallComponentWillUnmount(
finishedWork,
finishedWork.return,
instance,
);
}
recursivelyTraverseDisappearLayoutEffects(finishedWork);
break;
}
case HostHoistable:
case HostSingleton:
case HostComponent: {
safelyDetachRef(finishedWork, finishedWork.return);
recursivelyTraverseDisappearLayoutEffects(finishedWork);
break;
}
case OffscreenComponent: {
safelyDetachRef(finishedWork, finishedWork.return);
const isHidden = finishedWork.memoizedState !== null;
if (isHidden) {
} else {
recursivelyTraverseDisappearLayoutEffects(finishedWork);
}
break;
}
default: {
recursivelyTraverseDisappearLayoutEffects(finishedWork);
break;
}
}
}
function recursivelyTraverseDisappearLayoutEffects(parentFiber: Fiber) {
let child = parentFiber.child;
while (child !== null) {
disappearLayoutEffects(child);
child = child.sibling;
}
}
export function reappearLayoutEffects(
finishedRoot: FiberRoot,
current: Fiber | null,
finishedWork: Fiber,
includeWorkInProgressEffects: boolean,
) {
const flags = finishedWork.flags;
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
recursivelyTraverseReappearLayoutEffects(
finishedRoot,
finishedWork,
includeWorkInProgressEffects,
);
commitHookLayoutEffects(finishedWork, HookLayout);
break;
}
case ClassComponent: {
recursivelyTraverseReappearLayoutEffects(
finishedRoot,
finishedWork,
includeWorkInProgressEffects,
);
commitClassDidMount(finishedWork);
commitClassHiddenCallbacks(finishedWork);
if (includeWorkInProgressEffects && flags & Callback) {
commitClassCallbacks(finishedWork);
}
safelyAttachRef(finishedWork, finishedWork.return);
break;
}
case HostHoistable:
case HostSingleton:
case HostComponent: {
recursivelyTraverseReappearLayoutEffects(
finishedRoot,
finishedWork,
includeWorkInProgressEffects,
);
if (includeWorkInProgressEffects && current === null && flags & Update) {
commitHostMount(finishedWork);
}
safelyAttachRef(finishedWork, finishedWork.return);
break;
}
case Profiler: {
if (includeWorkInProgressEffects && flags & Update) {
const prevEffectDuration = pushNestedEffectDurations();
recursivelyTraverseReappearLayoutEffects(
finishedRoot,
finishedWork,
includeWorkInProgressEffects,
);
const profilerInstance = finishedWork.stateNode;
if (enableProfilerTimer && enableProfilerCommitHooks) {
profilerInstance.effectDuration +=
bubbleNestedEffectDurations(prevEffectDuration);
}
commitProfilerUpdate(
finishedWork,
current,
commitStartTime,
profilerInstance.effectDuration,
);
} else {
recursivelyTraverseReappearLayoutEffects(
finishedRoot,
finishedWork,
includeWorkInProgressEffects,
);
}
break;
}
case SuspenseComponent: {
recursivelyTraverseReappearLayoutEffects(
finishedRoot,
finishedWork,
includeWorkInProgressEffects,
);
if (includeWorkInProgressEffects && flags & Update) {
commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);
}
break;
}
case OffscreenComponent: {
const offscreenState: OffscreenState = finishedWork.memoizedState;
const isHidden = offscreenState !== null;
if (isHidden) {
} else {
recursivelyTraverseReappearLayoutEffects(
finishedRoot,
finishedWork,
includeWorkInProgressEffects,
);
}
safelyAttachRef(finishedWork, finishedWork.return);
break;
}
default: {
recursivelyTraverseReappearLayoutEffects(
finishedRoot,
finishedWork,
includeWorkInProgressEffects,
);
break;
}
}
}
function recursivelyTraverseReappearLayoutEffects(
finishedRoot: FiberRoot,
parentFiber: Fiber,
includeWorkInProgressEffects: boolean,
) {
const childShouldIncludeWorkInProgressEffects =
includeWorkInProgressEffects &&
(parentFiber.subtreeFlags & LayoutMask) !== NoFlags;
let child = parentFiber.child;
while (child !== null) {
const current = child.alternate;
reappearLayoutEffects(
finishedRoot,
current,
child,
childShouldIncludeWorkInProgressEffects,
);
child = child.sibling;
}
}
function commitOffscreenPassiveMountEffects(
current: Fiber | null,
finishedWork: Fiber,
instance: OffscreenInstance,
) {
if (enableCache) {
let previousCache: Cache | null = null;
if (
current !== null &&
current.memoizedState !== null &&
current.memoizedState.cachePool !== null
) {
previousCache = current.memoizedState.cachePool.pool;
}
let nextCache: Cache | null = null;
if (
finishedWork.memoizedState !== null &&
finishedWork.memoizedState.cachePool !== null
) {
nextCache = finishedWork.memoizedState.cachePool.pool;
}
if (nextCache !== previousCache) {
if (nextCache != null) {
retainCache(nextCache);
}
if (previousCache != null) {
releaseCache(previousCache);
}
}
}
if (enableTransitionTracing) {
const offscreenState: OffscreenState = finishedWork.memoizedState;
const queue: OffscreenQueue | null = (finishedWork.updateQueue: any);
const isHidden = offscreenState !== null;
if (queue !== null) {
if (isHidden) {
const transitions = queue.transitions;
if (transitions !== null) {
transitions.forEach(transition => {
if (instance._transitions === null) {
instance._transitions = new Set();
}
instance._transitions.add(transition);
});
}
const markerInstances = queue.markerInstances;
if (markerInstances !== null) {
markerInstances.forEach(markerInstance => {
const markerTransitions = markerInstance.transitions;
if (markerTransitions !== null) {
markerTransitions.forEach(transition => {
if (instance._transitions === null) {
instance._transitions = new Set();
} else if (instance._transitions.has(transition)) {
if (markerInstance.pendingBoundaries === null) {
markerInstance.pendingBoundaries = new Map();
}
if (instance._pendingMarkers === null) {
instance._pendingMarkers = new Set();
}
instance._pendingMarkers.add(markerInstance);
}
});
}
});
}
}
finishedWork.updateQueue = null;
}
commitTransitionProgress(finishedWork);
if (!isHidden) {
instance._transitions = null;
instance._pendingMarkers = null;
}
}
}
function commitCachePassiveMountEffect(
current: Fiber | null,
finishedWork: Fiber,
) {
if (enableCache) {
let previousCache: Cache | null = null;
if (finishedWork.alternate !== null) {
previousCache = finishedWork.alternate.memoizedState.cache;
}
const nextCache = finishedWork.memoizedState.cache;
if (nextCache !== previousCache) {
retainCache(nextCache);
if (previousCache != null) {
releaseCache(previousCache);
}
}
}
}
function commitTracingMarkerPassiveMountEffect(finishedWork: Fiber) {
const instance = finishedWork.stateNode;
if (instance.transitions !== null && instance.pendingBoundaries === null) {
addMarkerCompleteCallbackToPendingTransition(
finishedWork.memoizedProps.name,
instance.transitions,
);
instance.transitions = null;
instance.pendingBoundaries = null;
instance.aborts = null;
instance.name = null;
}
}
export function commitPassiveMountEffects(
root: FiberRoot,
finishedWork: Fiber,
committedLanes: Lanes,
committedTransitions: Array<Transition> | null,
renderEndTime: number,
): void {
resetComponentEffectTimers();
commitPassiveMountOnFiber(
root,
finishedWork,
committedLanes,
committedTransitions,
enableProfilerTimer && enableComponentPerformanceTrack ? renderEndTime : 0,
);
}
function recursivelyTraversePassiveMountEffects(
root: FiberRoot,
parentFiber: Fiber,
committedLanes: Lanes,
committedTransitions: Array<Transition> | null,
endTime: number,
) {
if (
parentFiber.subtreeFlags & PassiveMask ||
(enableProfilerTimer &&
enableComponentPerformanceTrack &&
parentFiber.actualDuration !== 0 &&
(parentFiber.alternate === null ||
parentFiber.alternate.child !== parentFiber.child))
) {
let child = parentFiber.child;
while (child !== null) {
if (enableProfilerTimer && enableComponentPerformanceTrack) {
const nextSibling = child.sibling;
commitPassiveMountOnFiber(
root,
child,
committedLanes,
committedTransitions,
nextSibling !== null
? ((nextSibling.actualStartTime: any): number)
: endTime,
);
child = nextSibling;
} else {
commitPassiveMountOnFiber(
root,
child,
committedLanes,
committedTransitions,
0,
);
child = child.sibling;
}
}
}
}
function commitPassiveMountOnFiber(
finishedRoot: FiberRoot,
finishedWork: Fiber,
committedLanes: Lanes,
committedTransitions: Array<Transition> | null,
endTime: number,
): void {
const prevEffectStart = pushComponentEffectStart();
if (
enableProfilerTimer &&
enableComponentPerformanceTrack &&
(finishedWork.mode & ProfileMode) !== NoMode &&
((finishedWork.actualStartTime: any): number) > 0
) {
logComponentRender(
finishedWork,
((finishedWork.actualStartTime: any): number),
endTime,
);
}
const flags = finishedWork.flags;
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
recursivelyTraversePassiveMountEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
endTime,
);
if (flags & Passive) {
commitHookPassiveMountEffects(
finishedWork,
HookPassive | HookHasEffect,
);
}
break;
}
case HostRoot: {
const prevEffectDuration = pushNestedEffectDurations();
recursivelyTraversePassiveMountEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
endTime,
);
if (flags & Passive) {
if (enableCache) {
let previousCache: Cache | null = null;
if (finishedWork.alternate !== null) {
previousCache = finishedWork.alternate.memoizedState.cache;
}
const nextCache = finishedWork.memoizedState.cache;
if (nextCache !== previousCache) {
retainCache(nextCache);
if (previousCache != null) {
releaseCache(previousCache);
}
}
}
if (enableTransitionTracing) {
const root: FiberRoot = finishedWork.stateNode;
const incompleteTransitions = root.incompleteTransitions;
if (committedTransitions !== null) {
committedTransitions.forEach(transition => {
addTransitionStartCallbackToPendingTransition(transition);
});
clearTransitionsForLanes(finishedRoot, committedLanes);
}
incompleteTransitions.forEach((markerInstance, transition) => {
const pendingBoundaries = markerInstance.pendingBoundaries;
if (pendingBoundaries === null || pendingBoundaries.size === 0) {
if (markerInstance.aborts === null) {
addTransitionCompleteCallbackToPendingTransition(transition);
}
incompleteTransitions.delete(transition);
}
});
clearTransitionsForLanes(finishedRoot, committedLanes);
}
}
if (enableProfilerTimer && enableProfilerCommitHooks) {
finishedRoot.passiveEffectDuration +=
popNestedEffectDurations(prevEffectDuration);
}
break;
}
case Profiler: {
if (flags & Passive) {
const prevEffectDuration = pushNestedEffectDurations();
recursivelyTraversePassiveMountEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
endTime,
);
const profilerInstance = finishedWork.stateNode;
if (enableProfilerTimer && enableProfilerCommitHooks) {
profilerInstance.passiveEffectDuration +=
bubbleNestedEffectDurations(prevEffectDuration);
}
commitProfilerPostCommit(
finishedWork,
finishedWork.alternate,
commitStartTime,
profilerInstance.passiveEffectDuration,
);
} else {
recursivelyTraversePassiveMountEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
endTime,
);
}
break;
}
case LegacyHiddenComponent: {
if (enableLegacyHidden) {
recursivelyTraversePassiveMountEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
endTime,
);
if (flags & Passive) {
const current = finishedWork.alternate;
const instance: OffscreenInstance = finishedWork.stateNode;
commitOffscreenPassiveMountEffects(current, finishedWork, instance);
}
}
break;
}
case OffscreenComponent: {
const instance: OffscreenInstance = finishedWork.stateNode;
const nextState: OffscreenState | null = finishedWork.memoizedState;
const isHidden = nextState !== null;
if (isHidden) {
if (instance._visibility & OffscreenPassiveEffectsConnected) {
recursivelyTraversePassiveMountEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
endTime,
);
} else {
if (disableLegacyMode || finishedWork.mode & ConcurrentMode) {
if (enableCache || enableTransitionTracing) {
recursivelyTraverseAtomicPassiveEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
);
}
} else {
instance._visibility |= OffscreenPassiveEffectsConnected;
recursivelyTraversePassiveMountEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
endTime,
);
}
}
} else {
if (instance._visibility & OffscreenPassiveEffectsConnected) {
recursivelyTraversePassiveMountEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
endTime,
);
} else {
instance._visibility |= OffscreenPassiveEffectsConnected;
const includeWorkInProgressEffects =
(finishedWork.subtreeFlags & PassiveMask) !== NoFlags;
recursivelyTraverseReconnectPassiveEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
includeWorkInProgressEffects,
);
}
}
if (flags & Passive) {
const current = finishedWork.alternate;
commitOffscreenPassiveMountEffects(current, finishedWork, instance);
}
break;
}
case CacheComponent: {
recursivelyTraversePassiveMountEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
endTime,
);
if (flags & Passive) {
const current = finishedWork.alternate;
commitCachePassiveMountEffect(current, finishedWork);
}
break;
}
case TracingMarkerComponent: {
if (enableTransitionTracing) {
recursivelyTraversePassiveMountEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
endTime,
);
if (flags & Passive) {
commitTracingMarkerPassiveMountEffect(finishedWork);
}
break;
}
}
default: {
recursivelyTraversePassiveMountEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
endTime,
);
break;
}
}
if (
enableProfilerTimer &&
enableProfilerCommitHooks &&
enableComponentPerformanceTrack &&
(finishedWork.mode & ProfileMode) !== NoMode &&
componentEffectStartTime >= 0 &&
componentEffectEndTime >= 0
) {
logComponentEffect(
finishedWork,
componentEffectStartTime,
componentEffectEndTime,
componentEffectDuration,
);
}
popComponentEffectStart(prevEffectStart);
}
function recursivelyTraverseReconnectPassiveEffects(
finishedRoot: FiberRoot,
parentFiber: Fiber,
committedLanes: Lanes,
committedTransitions: Array<Transition> | null,
includeWorkInProgressEffects: boolean,
) {
const childShouldIncludeWorkInProgressEffects =
includeWorkInProgressEffects &&
(parentFiber.subtreeFlags & PassiveMask) !== NoFlags;
let child = parentFiber.child;
while (child !== null) {
reconnectPassiveEffects(
finishedRoot,
child,
committedLanes,
committedTransitions,
childShouldIncludeWorkInProgressEffects,
);
child = child.sibling;
}
}
export function reconnectPassiveEffects(
finishedRoot: FiberRoot,
finishedWork: Fiber,
committedLanes: Lanes,
committedTransitions: Array<Transition> | null,
includeWorkInProgressEffects: boolean,
) {
const flags = finishedWork.flags;
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
recursivelyTraverseReconnectPassiveEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
includeWorkInProgressEffects,
);
commitHookPassiveMountEffects(finishedWork, HookPassive);
break;
}
case LegacyHiddenComponent: {
if (enableLegacyHidden) {
recursivelyTraverseReconnectPassiveEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
includeWorkInProgressEffects,
);
if (includeWorkInProgressEffects && flags & Passive) {
const current: Fiber | null = finishedWork.alternate;
const instance: OffscreenInstance = finishedWork.stateNode;
commitOffscreenPassiveMountEffects(current, finishedWork, instance);
}
}
break;
}
case OffscreenComponent: {
const instance: OffscreenInstance = finishedWork.stateNode;
const nextState: OffscreenState | null = finishedWork.memoizedState;
const isHidden = nextState !== null;
if (isHidden) {
if (instance._visibility & OffscreenPassiveEffectsConnected) {
recursivelyTraverseReconnectPassiveEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
includeWorkInProgressEffects,
);
} else {
if (disableLegacyMode || finishedWork.mode & ConcurrentMode) {
if (enableCache || enableTransitionTracing) {
recursivelyTraverseAtomicPassiveEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
);
}
} else {
instance._visibility |= OffscreenPassiveEffectsConnected;
recursivelyTraverseReconnectPassiveEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
includeWorkInProgressEffects,
);
}
}
} else {
instance._visibility |= OffscreenPassiveEffectsConnected;
recursivelyTraverseReconnectPassiveEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
includeWorkInProgressEffects,
);
}
if (includeWorkInProgressEffects && flags & Passive) {
const current: Fiber | null = finishedWork.alternate;
commitOffscreenPassiveMountEffects(current, finishedWork, instance);
}
break;
}
case CacheComponent: {
recursivelyTraverseReconnectPassiveEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
includeWorkInProgressEffects,
);
if (includeWorkInProgressEffects && flags & Passive) {
const current = finishedWork.alternate;
commitCachePassiveMountEffect(current, finishedWork);
}
break;
}
case TracingMarkerComponent: {
if (enableTransitionTracing) {
recursivelyTraverseReconnectPassiveEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
includeWorkInProgressEffects,
);
if (includeWorkInProgressEffects && flags & Passive) {
commitTracingMarkerPassiveMountEffect(finishedWork);
}
break;
}
}
default: {
recursivelyTraverseReconnectPassiveEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
includeWorkInProgressEffects,
);
break;
}
}
}
function recursivelyTraverseAtomicPassiveEffects(
finishedRoot: FiberRoot,
parentFiber: Fiber,
committedLanes: Lanes,
committedTransitions: Array<Transition> | null,
) {
if (parentFiber.subtreeFlags & PassiveMask) {
let child = parentFiber.child;
while (child !== null) {
commitAtomicPassiveEffects(
finishedRoot,
child,
committedLanes,
committedTransitions,
);
child = child.sibling;
}
}
}
function commitAtomicPassiveEffects(
finishedRoot: FiberRoot,
finishedWork: Fiber,
committedLanes: Lanes,
committedTransitions: Array<Transition> | null,
) {
const flags = finishedWork.flags;
switch (finishedWork.tag) {
case OffscreenComponent: {
recursivelyTraverseAtomicPassiveEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
);
if (flags & Passive) {
const current = finishedWork.alternate;
const instance: OffscreenInstance = finishedWork.stateNode;
commitOffscreenPassiveMountEffects(current, finishedWork, instance);
}
break;
}
case CacheComponent: {
recursivelyTraverseAtomicPassiveEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
);
if (flags & Passive) {
const current = finishedWork.alternate;
commitCachePassiveMountEffect(current, finishedWork);
}
break;
}
default: {
recursivelyTraverseAtomicPassiveEffects(
finishedRoot,
finishedWork,
committedLanes,
committedTransitions,
);
break;
}
}
}
export function commitPassiveUnmountEffects(finishedWork: Fiber): void {
resetComponentEffectTimers();
commitPassiveUnmountOnFiber(finishedWork);
}
let suspenseyCommitFlag = ShouldSuspendCommit;
export function accumulateSuspenseyCommit(finishedWork: Fiber): void {
accumulateSuspenseyCommitOnFiber(finishedWork);
}
function recursivelyAccumulateSuspenseyCommit(parentFiber: Fiber): void {
if (parentFiber.subtreeFlags & suspenseyCommitFlag) {
let child = parentFiber.child;
while (child !== null) {
accumulateSuspenseyCommitOnFiber(child);
child = child.sibling;
}
}
}
function accumulateSuspenseyCommitOnFiber(fiber: Fiber) {
switch (fiber.tag) {
case HostHoistable: {
recursivelyAccumulateSuspenseyCommit(fiber);
if (fiber.flags & suspenseyCommitFlag) {
if (fiber.memoizedState !== null) {
suspendResource(
(currentHoistableRoot: any),
fiber.memoizedState,
fiber.memoizedProps,
);
} else {
const type = fiber.type;
const props = fiber.memoizedProps;
suspendInstance(type, props);
}
}
break;
}
case HostComponent: {
recursivelyAccumulateSuspenseyCommit(fiber);
if (fiber.flags & suspenseyCommitFlag) {
const type = fiber.type;
const props = fiber.memoizedProps;
suspendInstance(type, props);
}
break;
}
case HostRoot:
case HostPortal: {
if (supportsResources) {
const previousHoistableRoot = currentHoistableRoot;
const container: Container = fiber.stateNode.containerInfo;
currentHoistableRoot = getHoistableRoot(container);
recursivelyAccumulateSuspenseyCommit(fiber);
currentHoistableRoot = previousHoistableRoot;
} else {
recursivelyAccumulateSuspenseyCommit(fiber);
}
break;
}
case OffscreenComponent: {
const isHidden = (fiber.memoizedState: OffscreenState | null) !== null;
if (isHidden) {
} else {
const current = fiber.alternate;
const wasHidden =
current !== null &&
(current.memoizedState: OffscreenState | null) !== null;
if (wasHidden) {
const prevFlags = suspenseyCommitFlag;
suspenseyCommitFlag = MaySuspendCommit;
recursivelyAccumulateSuspenseyCommit(fiber);
suspenseyCommitFlag = prevFlags;
} else {
recursivelyAccumulateSuspenseyCommit(fiber);
}
}
break;
}
default: {
recursivelyAccumulateSuspenseyCommit(fiber);
}
}
}
function detachAlternateSiblings(parentFiber: Fiber) {
const previousFiber = parentFiber.alternate;
if (previousFiber !== null) {
let detachedChild = previousFiber.child;
if (detachedChild !== null) {
previousFiber.child = null;
do {
const detachedSibling = detachedChild.sibling;
detachedChild.sibling = null;
detachedChild = detachedSibling;
} while (detachedChild !== null);
}
}
}
function recursivelyTraversePassiveUnmountEffects(parentFiber: Fiber): void {
const deletions = parentFiber.deletions;
if ((parentFiber.flags & ChildDeletion) !== NoFlags) {
if (deletions !== null) {
for (let i = 0; i < deletions.length; i++) {
const childToDelete = deletions[i];
nextEffect = childToDelete;
commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
childToDelete,
parentFiber,
);
}
}
detachAlternateSiblings(parentFiber);
}
if (parentFiber.subtreeFlags & PassiveMask) {
let child = parentFiber.child;
while (child !== null) {
commitPassiveUnmountOnFiber(child);
child = child.sibling;
}
}
}
function commitPassiveUnmountOnFiber(finishedWork: Fiber): void {
const prevEffectStart = pushComponentEffectStart();
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
recursivelyTraversePassiveUnmountEffects(finishedWork);
if (finishedWork.flags & Passive) {
commitHookPassiveUnmountEffects(
finishedWork,
finishedWork.return,
HookPassive | HookHasEffect,
);
}
break;
}
case HostRoot: {
const prevEffectDuration = pushNestedEffectDurations();
recursivelyTraversePassiveUnmountEffects(finishedWork);
if (enableProfilerTimer && enableProfilerCommitHooks) {
const finishedRoot: FiberRoot = finishedWork.stateNode;
finishedRoot.passiveEffectDuration +=
popNestedEffectDurations(prevEffectDuration);
}
break;
}
case Profiler: {
const prevEffectDuration = pushNestedEffectDurations();
recursivelyTraversePassiveUnmountEffects(finishedWork);
if (enableProfilerTimer && enableProfilerCommitHooks) {
const profilerInstance = finishedWork.stateNode;
profilerInstance.passiveEffectDuration +=
bubbleNestedEffectDurations(prevEffectDuration);
}
break;
}
case OffscreenComponent: {
const instance: OffscreenInstance = finishedWork.stateNode;
const nextState: OffscreenState | null = finishedWork.memoizedState;
const isHidden = nextState !== null;
if (
isHidden &&
instance._visibility & OffscreenPassiveEffectsConnected &&
(finishedWork.return === null ||
finishedWork.return.tag !== SuspenseComponent)
) {
instance._visibility &= ~OffscreenPassiveEffectsConnected;
recursivelyTraverseDisconnectPassiveEffects(finishedWork);
} else {
recursivelyTraversePassiveUnmountEffects(finishedWork);
}
break;
}
default: {
recursivelyTraversePassiveUnmountEffects(finishedWork);
break;
}
}
if (
enableProfilerTimer &&
enableProfilerCommitHooks &&
enableComponentPerformanceTrack &&
(finishedWork.mode & ProfileMode) !== NoMode &&
componentEffectStartTime >= 0 &&
componentEffectEndTime >= 0
) {
logComponentEffect(
finishedWork,
componentEffectStartTime,
componentEffectEndTime,
componentEffectDuration,
);
}
popComponentEffectStart(prevEffectStart);
}
function recursivelyTraverseDisconnectPassiveEffects(parentFiber: Fiber): void {
const deletions = parentFiber.deletions;
if ((parentFiber.flags & ChildDeletion) !== NoFlags) {
if (deletions !== null) {
for (let i = 0; i < deletions.length; i++) {
const childToDelete = deletions[i];
nextEffect = childToDelete;
commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
childToDelete,
parentFiber,
);
}
}
detachAlternateSiblings(parentFiber);
}
let child = parentFiber.child;
while (child !== null) {
disconnectPassiveEffect(child);
child = child.sibling;
}
}
export function disconnectPassiveEffect(finishedWork: Fiber): void {
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
commitHookPassiveUnmountEffects(
finishedWork,
finishedWork.return,
HookPassive,
);
recursivelyTraverseDisconnectPassiveEffects(finishedWork);
break;
}
case OffscreenComponent: {
const instance: OffscreenInstance = finishedWork.stateNode;
if (instance._visibility & OffscreenPassiveEffectsConnected) {
instance._visibility &= ~OffscreenPassiveEffectsConnected;
recursivelyTraverseDisconnectPassiveEffects(finishedWork);
} else {
}
break;
}
default: {
recursivelyTraverseDisconnectPassiveEffects(finishedWork);
break;
}
}
}
function commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
deletedSubtreeRoot: Fiber,
nearestMountedAncestor: Fiber | null,
) {
while (nextEffect !== null) {
const fiber = nextEffect;
commitPassiveUnmountInsideDeletedTreeOnFiber(fiber, nearestMountedAncestor);
const child = fiber.child;
if (child !== null) {
child.return = fiber;
nextEffect = child;
} else {
commitPassiveUnmountEffectsInsideOfDeletedTree_complete(
deletedSubtreeRoot,
);
}
}
}
function commitPassiveUnmountEffectsInsideOfDeletedTree_complete(
deletedSubtreeRoot: Fiber,
) {
while (nextEffect !== null) {
const fiber = nextEffect;
const sibling = fiber.sibling;
const returnFiber = fiber.return;
detachFiberAfterEffects(fiber);
if (fiber === deletedSubtreeRoot) {
nextEffect = null;
return;
}
if (sibling !== null) {
sibling.return = returnFiber;
nextEffect = sibling;
return;
}
nextEffect = returnFiber;
}
}
function commitPassiveUnmountInsideDeletedTreeOnFiber(
current: Fiber,
nearestMountedAncestor: Fiber | null,
): void {
switch (current.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
commitHookPassiveUnmountEffects(
current,
nearestMountedAncestor,
HookPassive,
);
break;
}
case LegacyHiddenComponent:
case OffscreenComponent: {
if (enableCache) {
if (
current.memoizedState !== null &&
current.memoizedState.cachePool !== null
) {
const cache: Cache = current.memoizedState.cachePool.pool;
if (cache != null) {
retainCache(cache);
}
}
}
break;
}
case SuspenseComponent: {
if (enableTransitionTracing) {
const offscreenFiber: Fiber = (current.child: any);
const instance: OffscreenInstance = offscreenFiber.stateNode;
const transitions = instance._transitions;
if (transitions !== null) {
const abortReason = {
reason: 'suspense',
name: current.memoizedProps.unstable_name || null,
};
if (
current.memoizedState === null ||
current.memoizedState.dehydrated === null
) {
abortParentMarkerTransitionsForDeletedFiber(
offscreenFiber,
abortReason,
transitions,
instance,
true,
);
if (nearestMountedAncestor !== null) {
abortParentMarkerTransitionsForDeletedFiber(
nearestMountedAncestor,
abortReason,
transitions,
instance,
false,
);
}
}
}
}
break;
}
case CacheComponent: {
if (enableCache) {
const cache = current.memoizedState.cache;
releaseCache(cache);
}
break;
}
case TracingMarkerComponent: {
if (enableTransitionTracing) {
const instance: TracingMarkerInstance = current.stateNode;
const transitions = instance.transitions;
if (transitions !== null) {
const abortReason = {
reason: 'marker',
name: current.memoizedProps.name,
};
abortParentMarkerTransitionsForDeletedFiber(
current,
abortReason,
transitions,
null,
true,
);
if (nearestMountedAncestor !== null) {
abortParentMarkerTransitionsForDeletedFiber(
nearestMountedAncestor,
abortReason,
transitions,
null,
false,
);
}
}
}
break;
}
}
}
export function invokeLayoutEffectMountInDEV(fiber: Fiber): void {
if (__DEV__) {
switch (fiber.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
commitHookEffectListMount(HookLayout | HookHasEffect, fiber);
break;
}
case ClassComponent: {
commitClassDidMount(fiber);
break;
}
}
}
}
export function invokePassiveEffectMountInDEV(fiber: Fiber): void {
if (__DEV__) {
switch (fiber.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
commitHookEffectListMount(HookPassive | HookHasEffect, fiber);
break;
}
}
}
}
export function invokeLayoutEffectUnmountInDEV(fiber: Fiber): void {
if (__DEV__) {
switch (fiber.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
commitHookEffectListUnmount(
HookLayout | HookHasEffect,
fiber,
fiber.return,
);
break;
}
case ClassComponent: {
const instance = fiber.stateNode;
if (typeof instance.componentWillUnmount === 'function') {
safelyCallComponentWillUnmount(fiber, fiber.return, instance);
}
break;
}
}
}
}
export function invokePassiveEffectUnmountInDEV(fiber: Fiber): void {
if (__DEV__) {
switch (fiber.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
commitHookEffectListUnmount(
HookPassive | HookHasEffect,
fiber,
fiber.return,
);
}
}
}
}