do { // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which // means `flushPassiveEffects` will sometimes result in additional // passive effects. So we need to keep flushing in a loop until there are // no more pending effects. // TODO: Might be better if `flushPassiveEffects` did not automatically // flush synchronous work at the end, to avoid factoring hazards like this. flushPendingEffects(); } while (pendingEffectsStatus !== NO_PENDING_EFFECTS); flushRenderPhaseStrictModeWarningsInDEV(); // Check which lanes no longer have any work scheduled on them, and mark let passiveSubtreeMask;
passiveSubtreeMask = PassiveMask;
if ( (finishedWork.subtreeFlags & passiveSubtreeMask) !== NoFlags || (finishedWork.flags & passiveSubtreeMask) !== NoFlags ) { root.callbackNode = null; root.callbackPriority = NoLane; scheduleCallback(NormalSchedulerPriority, () => { flushPassiveEffects(true); // This render triggered passive effects: release the root cache pool // *after* passive effects fire to avoid freeing a cache pool that may // be referenced by a node in the tree (HostRoot, Cache boundary etc) returnnull; }); } else { // If we don't have passive effects, we're not going to need to perform more work // so we can clear the callback now. root.callbackNode = null; root.callbackPriority = NoLane; }
if (subtreeMutationHasEffects || rootMutationHasEffect) { const prevTransition = ReactSharedInternals.T; ReactSharedInternals.T = null; const previousPriority = getCurrentUpdatePriority(); setCurrentUpdatePriority(DiscreteEventPriority); const prevExecutionContext = executionContext; executionContext |= CommitContext; try { // The next phase is the mutation phase, where we mutate the host tree. commitMutationEffects(root, finishedWork, lanes);
if (enableCreateEventHandleAPI) { if (shouldFireAfterActiveInstanceBlur) { afterActiveInstanceBlur(); } } resetAfterCommit(root.containerInfo); } finally { // Reset the priority to the previous non-sync value. executionContext = prevExecutionContext; setCurrentUpdatePriority(previousPriority); ReactSharedInternals.T = prevTransition; } }
// The work-in-progress tree is now the current tree. This must come after // the mutation phase, so that the previous tree is still current during // componentWillUnmount, but before the layout phase, so that the finished // work is current during componentDidMount/Update. root.current = finishedWork; pendingEffectsStatus = PENDING_LAYOUT_PHASE; }
if (flags & Update) { commitHookEffectListUnmount( HookInsertion | HookHasEffect, finishedWork, finishedWork.return, ); // TODO: Use a commitHookInsertionUnmountEffects wrapper to record timings. commitHookEffectListMount(HookInsertion | HookHasEffect, finishedWork); commitHookLayoutUnmountEffects( finishedWork, finishedWork.return, HookLayout | HookHasEffect, ); } break; } caseHostComponent: { recursivelyTraverseMutationEffects(root, finishedWork, lanes);
commitReconciliationEffects(finishedWork, lanes);
if (finishedWork.flags & ContentReset) { commitHostResetTextContent(finishedWork); }
if (flags & Update) { constinstance: Instance = finishedWork.stateNode; if (instance != null) { // Commit the work prepared earlier. // For hydration we reuse the update path but we treat the oldProps // as the newProps. The updatePayload will contain the real change in // this case. const newProps = finishedWork.memoizedProps; const oldProps = current !== null ? current.memoizedProps : newProps; commitHostUpdate(finishedWork, newProps, oldProps); } } break; } caseHostText: { recursivelyTraverseMutationEffects(root, finishedWork, lanes); commitReconciliationEffects(finishedWork, lanes);
if (flags & Update) { constnewText: string = finishedWork.memoizedProps; // For hydration we reuse the update path but we treat the oldProps // as the newProps. The updatePayload will contain the real change in // this case. constoldText: string = current !== null ? current.memoizedProps : newText;
functionrecursivelyTraverseMutationEffects( root: FiberRoot, parentFiber: Fiber, lanes: Lanes, ) { // Deletions effects can be scheduled on any fiber type. They need to happen // before the children effects have fired. const deletions = parentFiber.deletions; if (deletions !== null) { for (let i = 0; i < deletions.length; i++) { const childToDelete = deletions[i]; commitDeletionEffects(root, parentFiber, childToDelete); } }
functioncommitDeletionEffectsOnFiber( finishedRoot: FiberRoot, nearestMountedAncestor: Fiber, deletedFiber: Fiber, ) { // TODO: Delete this Hook once new DevTools ships everywhere. No longer needed. onCommitUnmount(deletedFiber);
// The cases in this outer switch modify the stack before they traverse // into their subtree. There are simpler cases in the inner switch // that don't modify the stack. switch (deletedFiber.tag) { caseHostComponent: caseHostText: { // We only need to remove the nearest host child. Set the host parent // to `null` on the stack to indicate that nested children don't // need to be removed. const prevHostParent = hostParent; const prevHostParentIsContainer = hostParentIsContainer; hostParent = null; recursivelyTraverseDeletionEffects( finishedRoot, nearestMountedAncestor, deletedFiber, ); hostParent = prevHostParent; hostParentIsContainer = prevHostParentIsContainer; if (hostParent !== null) { // Now that all the child effects have unmounted, we can remove the // node from the tree. commitHostRemoveChild( deletedFiber, nearestMountedAncestor, ((hostParent: any): Instance), (deletedFiber.stateNode: Instance | TextInstance), ); } return; } caseFunctionComponent: caseForwardRef: caseMemoComponent: caseSimpleMemoComponent: { commitHookEffectListUnmount( HookInsertion, deletedFiber, nearestMountedAncestor, ); commitHookLayoutUnmountEffects( deletedFiber, nearestMountedAncestor, HookLayout, ); recursivelyTraverseDeletionEffects( finishedRoot, nearestMountedAncestor, deletedFiber, ); return; } default: { recursivelyTraverseDeletionEffects( finishedRoot, nearestMountedAncestor, deletedFiber, ); return; } } }
functioncommitReconciliationEffects( finishedWork: Fiber, committedLanes: Lanes, ) { // Placement effects (insertions, reorders) can be scheduled on any fiber // type. They needs to happen after the children effects have fired, but // before the effects on this fiber have fired. const flags = finishedWork.flags; if (flags & Placement) { commitHostPlacement(finishedWork); finishedWork.flags &= ~Placement; } if (flags & Hydrating) { finishedWork.flags &= ~Hydrating; } }
constparent: Instance = hostParentFiber.stateNode; if (hostParentFiber.flags & ContentReset) { // Reset the text content of the parent before doing any insertions resetTextContent(parent); // Clear ContentReset from the effect tag hostParentFiber.flags &= ~ContentReset; }
const before = getHostSibling(finishedWork); // We only have the top Fiber that was inserted but we need to recurse down its // children to find all the terminal nodes. insertOrAppendPlacementNode( finishedWork, before, parent, parentFragmentInstances, );