function commitRoot(root) {
var renderPriorityLevel = getCurrentPriorityLevel()
runWithPriority$1(
ImmediatePriority$1,
commitRootImpl.bind(null, root, renderPriorityLevel),
)
return null
}
function commitRootImpl(root, renderPriorityLevel) {
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.
flushPassiveEffects()
} while (rootWithPendingPassiveEffects !== null)
flushRenderPhaseStrictModeWarningsInDEV()
if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) {
{
throw Error('Should not already be working.')
}
}
var finishedWork = root.finishedWork
var lanes = root.finishedLanes
{
markCommitStarted(lanes)
}
if (finishedWork === null) {
{
markCommitStopped()
}
return null
}
root.finishedWork = null
root.finishedLanes = NoLanes
if (!(finishedWork !== root.current)) {
{
throw Error(
'Cannot commit the same tree as before. This error is likely caused by a bug in React. Please file an issue.',
)
}
} // commitRoot never returns a continuation; it always finishes synchronously.
// So we can clear these now to allow a new callback to be scheduled.
root.callbackNode = null // Update the first and last pending times on this root. The new first
// pending time is whatever is left on the root fiber.
var remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes)
markRootFinished(root, remainingLanes) // Clear already finished discrete updates in case that a later call of
// `flushDiscreteUpdates` starts a useless render pass which may cancels
// a scheduled timeout.
if (rootsWithPendingDiscreteUpdates !== null) {
if (
!hasDiscreteLanes(remainingLanes) &&
rootsWithPendingDiscreteUpdates.has(root)
) {
rootsWithPendingDiscreteUpdates.delete(root)
}
}
if (root === workInProgressRoot) {
// We can reset these now that they are finished.
workInProgressRoot = null
workInProgress = null
workInProgressRootRenderLanes = NoLanes
} // Get the list of effects.
var firstEffect
if (finishedWork.flags > PerformedWork) {
// A fiber's effect list consists only of its children, not itself. So if
// the root has an effect, we need to add it to the end of the list. The
// resulting list is the set that would belong to the root's parent, if it
// had one; that is, all the effects in the tree including the root.
if (finishedWork.lastEffect !== null) {
finishedWork.lastEffect.nextEffect = finishedWork
firstEffect = finishedWork.firstEffect
} else {
firstEffect = finishedWork
}
} else {
// There is no effect on the root.
firstEffect = finishedWork.firstEffect
}
if (firstEffect !== null) {
var prevExecutionContext = executionContext
executionContext |= CommitContext
var prevInteractions = pushInteractions(root) // Reset this to null before calling lifecycles
ReactCurrentOwner$2.current = null // The commit phase is broken into several sub-phases. We do a separate pass
// of the effect list for each phase: all mutation effects come before all
// layout effects, and so on.
// The first phase a "before mutation" phase. We use this phase to read the
// state of the host tree right before we mutate it. This is where
// getSnapshotBeforeUpdate is called.
focusedInstanceHandle = prepareForCommit(root.containerInfo)
shouldFireAfterActiveInstanceBlur = false
nextEffect = firstEffect
do {
{
invokeGuardedCallback(null, commitBeforeMutationEffects, null)
if (hasCaughtError()) {
if (!(nextEffect !== null)) {
{
throw Error('Should be working on an effect.')
}
}
var error = clearCaughtError()
captureCommitPhaseError(nextEffect, error)
nextEffect = nextEffect.nextEffect
}
}
} while (nextEffect !== null) // We no longer need to track the active instance fiber
focusedInstanceHandle = null
{
// Mark the current commit time to be shared by all Profilers in this
// batch. This enables them to be grouped later.
recordCommitTime()
} // The next phase is the mutation phase, where we mutate the host tree.
nextEffect = firstEffect
do {
{
invokeGuardedCallback(
null,
commitMutationEffects,
null,
root,
renderPriorityLevel,
)
if (hasCaughtError()) {
if (!(nextEffect !== null)) {
{
throw Error('Should be working on an effect.')
}
}
var _error = clearCaughtError()
captureCommitPhaseError(nextEffect, _error)
nextEffect = nextEffect.nextEffect
}
}
} while (nextEffect !== null)
resetAfterCommit(root.containerInfo) // 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 // The next phase is the layout phase, where we call effects that read
// the host tree after it's been mutated. The idiomatic use case for this is
// layout, but class component lifecycles also fire here for legacy reasons.
nextEffect = firstEffect
do {
{
invokeGuardedCallback(null, commitLayoutEffects, null, root, lanes)
if (hasCaughtError()) {
if (!(nextEffect !== null)) {
{
throw Error('Should be working on an effect.')
}
}
var _error2 = clearCaughtError()
captureCommitPhaseError(nextEffect, _error2)
nextEffect = nextEffect.nextEffect
}
}
} while (nextEffect !== null)
nextEffect = null // Tell Scheduler to yield at the end of the frame, so the browser has an
// opportunity to paint.
requestPaint()
{
popInteractions(prevInteractions)
}
executionContext = prevExecutionContext
} else {
// No effects.
root.current = finishedWork // Measure these anyway so the flamegraph explicitly shows that there were
// no effects.
// TODO: Maybe there's a better way to report this.
{
recordCommitTime()
}
}
var rootDidHavePassiveEffects = rootDoesHavePassiveEffects
if (rootDoesHavePassiveEffects) {
// This commit has passive effects. Stash a reference to them. But don't
// schedule a callback until after flushing layout work.
rootDoesHavePassiveEffects = false
rootWithPendingPassiveEffects = root
pendingPassiveEffectsLanes = lanes
pendingPassiveEffectsRenderPriority = renderPriorityLevel
} else {
// We are done with the effect chain at this point so let's clear the
// nextEffect pointers to assist with GC. If we have passive effects, we'll
// clear this in flushPassiveEffects.
nextEffect = firstEffect
while (nextEffect !== null) {
var nextNextEffect = nextEffect.nextEffect
nextEffect.nextEffect = null
if (nextEffect.flags & Deletion) {
detachFiberAfterEffects(nextEffect)
}
nextEffect = nextNextEffect
}
} // Read this again, since an effect might have updated it
remainingLanes = root.pendingLanes // Check if there's remaining work on this root
if (remainingLanes !== NoLanes) {
{
if (spawnedWorkDuringRender !== null) {
var expirationTimes = spawnedWorkDuringRender
spawnedWorkDuringRender = null
for (var i = 0; i < expirationTimes.length; i++) {
scheduleInteractions(
root,
expirationTimes[i],
root.memoizedInteractions,
)
}
}
schedulePendingInteractions(root, remainingLanes)
}
} else {
// If there's no remaining work, we can clear the set of already failed
// error boundaries.
legacyErrorBoundariesThatAlreadyFailed = null
}
{
if (!rootDidHavePassiveEffects) {
// If there are no passive effects, then we can complete the pending interactions.
// Otherwise, we'll wait until after the passive effects are flushed.
// Wait to do this until after remaining work has been scheduled,
// so that we don't prematurely signal complete for interactions when there's e.g. hidden work.
finishPendingInteractions(root, lanes)
}
}
if (remainingLanes === SyncLane) {
// Count the number of times the root synchronously re-renders without
// finishing. If there are too many, it indicates an infinite update loop.
if (root === rootWithNestedUpdates) {
nestedUpdateCount++
} else {
nestedUpdateCount = 0
rootWithNestedUpdates = root
}
} else {
nestedUpdateCount = 0
}
onCommitRoot(finishedWork.stateNode, renderPriorityLevel)
{
onCommitRoot$1()
} // Always call this before exiting `commitRoot`, to ensure that any
// additional work on this root is scheduled.
ensureRootIsScheduled(root, now())
if (hasUncaughtError) {
hasUncaughtError = false
var _error3 = firstUncaughtError
firstUncaughtError = null
throw _error3
}
if ((executionContext & LegacyUnbatchedContext) !== NoContext) {
{
markCommitStopped()
} // This is a legacy edge case. We just committed the initial mount of
// a ReactDOM.render-ed root inside of batchedUpdates. The commit fired
// synchronously, but layout updates should be deferred until the end
// of the batch.
return null
} // If layout work was scheduled, flush it now.
flushSyncCallbackQueue()
{
markCommitStopped()
}
return null
}