Best JavaScript code snippet using playwright-internal
ReactFiberWorkLoop.old.js
Source:ReactFiberWorkLoop.old.js
...178 }179 return findRetryLane(currentEventWipLanes);180 }181 function scheduleUpdateOnFiber(fiber, lane, eventTime) {182 checkForNestedUpdates();183 warnAboutRenderPhaseUpdatesInDEV(fiber);184 var root = markUpdateLaneFromFiberToRoot(fiber, lane);185 if (root === null) {186 warnAboutUpdateOnUnmountedFiberInDEV(fiber);187 return null;188 } // Mark that the root has a pending update.189 markRootUpdated(root, lane, eventTime);190 if (root === workInProgressRoot) {191 // Received an update to a tree that's in the middle of rendering. Mark192 // that there was an interleaved update work on this root. Unless the193 // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render194 // phase update. In that case, we don't treat render phase updates as if195 // they were interleaved, for backwards compat reasons.196 {197 workInProgressRootUpdatedLanes = mergeLanes(workInProgressRootUpdatedLanes, lane);198 }199 if (workInProgressRootExitStatus === RootSuspendedWithDelay) {200 // The root already suspended with a delay, which means this render201 // definitely won't finish. Since we have a new update, let's mark it as202 // suspended now, right before marking the incoming update. This has the203 // effect of interrupting the current render and switching to the update.204 // TODO: Make sure this doesn't override pings that happen while we've205 // already started rendering.206 markRootSuspended$1(root, workInProgressRootRenderLanes);207 }208 } // TODO: requestUpdateLanePriority also reads the priority. Pass the209 // priority as an argument to that function and this one.210 var priorityLevel = getCurrentPriorityLevel();211 if (lane === SyncLane) {212 if ( // Check if we're inside unbatchedUpdates213 (executionContext & LegacyUnbatchedContext) !== NoContext && // Check if we're not already rendering214 (executionContext & (RenderContext | CommitContext)) === NoContext) {215 // Register pending interactions on the root to avoid losing traced interaction data.216 schedulePendingInteractions(root, lane); // This is a legacy edge case. The initial mount of a ReactDOM.render-ed217 // root inside of batchedUpdates should be synchronous, but layout updates218 // should be deferred until the end of the batch.219 performSyncWorkOnRoot(root);220 } else {221 ensureRootIsScheduled(root, eventTime);222 schedulePendingInteractions(root, lane);223 if (executionContext === NoContext) {224 // Flush the synchronous work now, unless we're already working or inside225 // a batch. This is intentionally inside scheduleUpdateOnFiber instead of226 // scheduleCallbackForFiber to preserve the ability to schedule a callback227 // without immediately flushing it. We only do this for user-initiated228 // updates, to preserve historical behavior of legacy mode.229 resetRenderTimer();230 flushSyncCallbackQueue();231 }232 }233 } else {234 // Schedule a discrete update but only if it's not Sync.235 if ((executionContext & DiscreteEventContext) !== NoContext && ( // Only updates at user-blocking priority or greater are considered236 // discrete, even inside a discrete event.237 priorityLevel === UserBlockingPriority$2 || priorityLevel === ImmediatePriority$1)) {238 // This is the result of a discrete event. Track the lowest priority239 // discrete update per root so we can flush them early, if needed.240 if (rootsWithPendingDiscreteUpdates === null) {241 rootsWithPendingDiscreteUpdates = new Set([root]);242 } else {243 rootsWithPendingDiscreteUpdates.add(root);244 }245 } // Schedule other updates after in case the callback is sync.246 ensureRootIsScheduled(root, eventTime);247 schedulePendingInteractions(root, lane);248 } // We use this when assigning a lane for a transition inside249 // `requestUpdateLane`. We assume it's the same as the root being updated,250 // since in the common case of a single root app it probably is. If it's not251 // the same root, then it's not a huge deal, we just might batch more stuff252 // together more than necessary.253 mostRecentlyUpdatedRoot = root;254 } // This is split into a separate function so we can mark a fiber with pending255 // work without treating it as a typical update that originates from an event;256 // e.g. retrying a Suspense boundary isn't an update, but it does schedule work257 // on a fiber.258 function markUpdateLaneFromFiberToRoot(sourceFiber, lane) {259 // Update the source fiber's lanes260 sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);261 var alternate = sourceFiber.alternate;262 if (alternate !== null) {263 alternate.lanes = mergeLanes(alternate.lanes, lane);264 }265 {266 if (alternate === null && (sourceFiber.flags & (Placement | Hydrating)) !== NoFlags) {267 warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);268 }269 } // Walk the parent path to the root and update the child expiration time.270 var node = sourceFiber;271 var parent = sourceFiber.return;272 while (parent !== null) {273 parent.childLanes = mergeLanes(parent.childLanes, lane);274 alternate = parent.alternate;275 if (alternate !== null) {276 alternate.childLanes = mergeLanes(alternate.childLanes, lane);277 } else {278 {279 if ((parent.flags & (Placement | Hydrating)) !== NoFlags) {280 warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);281 }282 }283 }284 node = parent;285 parent = parent.return;286 }287 if (node.tag === HostRoot) {288 var root = node.stateNode;289 return root;290 } else {291 return null;292 }293 } // Use this function to schedule a task for a root. There's only one task per294 // root; if a task was already scheduled, we'll check to make sure the priority295 // of the existing task is the same as the priority of the next level that the296 // root has work on. This function is called on every update, and right before297 // exiting a task.298 function ensureRootIsScheduled(root, currentTime) {299 var existingCallbackNode = root.callbackNode; // Check if any lanes are being starved by other work. If so, mark them as300 // expired so we know to work on those next.301 markStarvedLanesAsExpired(root, currentTime); // Determine the next lanes to work on, and their priority.302 var nextLanes = getNextLanes(root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes); // This returns the priority level computed during the `getNextLanes` call.303 var newCallbackPriority = returnNextLanesPriority();304 if (nextLanes === NoLanes) {305 // Special case: There's nothing to work on.306 if (existingCallbackNode !== null) {307 cancelCallback(existingCallbackNode);308 root.callbackNode = null;309 root.callbackPriority = NoLanePriority;310 }311 return;312 } // Check if there's an existing task. We may be able to reuse it.313 if (existingCallbackNode !== null) {314 var existingCallbackPriority = root.callbackPriority;315 if (existingCallbackPriority === newCallbackPriority) {316 // The priority hasn't changed. We can reuse the existing task. Exit.317 return;318 } // The priority changed. Cancel the existing callback. We'll schedule a new319 // one below.320 cancelCallback(existingCallbackNode);321 } // Schedule a new callback.322 var newCallbackNode;323 if (newCallbackPriority === SyncLanePriority) {324 // Special case: Sync React callbacks are scheduled on a special325 // internal queue326 newCallbackNode = scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));327 } else if (newCallbackPriority === SyncBatchedLanePriority) {328 newCallbackNode = scheduleCallback(ImmediatePriority$1, performSyncWorkOnRoot.bind(null, root));329 } else {330 var schedulerPriorityLevel = lanePriorityToSchedulerPriority(newCallbackPriority);331 newCallbackNode = scheduleCallback(schedulerPriorityLevel, performConcurrentWorkOnRoot.bind(null, root));332 }333 root.callbackPriority = newCallbackPriority;334 root.callbackNode = newCallbackNode;335 } // This is the entry point for every concurrent task, i.e. anything that336 // goes through Scheduler.337 function performConcurrentWorkOnRoot(root) {338 // Since we know we're in a React event, we can clear the current339 // event time. The next update will compute a new event time.340 currentEventTime = NoTimestamp;341 currentEventWipLanes = NoLanes;342 currentEventPendingLanes = NoLanes;343 if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) {344 {345 throw Error( "Should not already be working." );346 }347 } // Flush any pending passive effects before deciding which lanes to work on,348 // in case they schedule additional work.349 var originalCallbackNode = root.callbackNode;350 var didFlushPassiveEffects = flushPassiveEffects();351 if (didFlushPassiveEffects) {352 // Something in the passive effect phase may have canceled the current task.353 // Check if the task node for this root was changed.354 if (root.callbackNode !== originalCallbackNode) {355 // The current task was canceled. Exit. We don't need to call356 // `ensureRootIsScheduled` because the check above implies either that357 // there's a new task, or that there's no remaining work on this root.358 return null;359 }360 } // Determine the next expiration time to work on, using the fields stored361 // on the root.362 var lanes = getNextLanes(root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes);363 if (lanes === NoLanes) {364 // Defensive coding. This is never expected to happen.365 return null;366 }367 var exitStatus = renderRootConcurrent(root, lanes);368 if (includesSomeLane(workInProgressRootIncludedLanes, workInProgressRootUpdatedLanes)) {369 // The render included lanes that were updated during the render phase.370 // For example, when unhiding a hidden tree, we include all the lanes371 // that were previously skipped when the tree was hidden. That set of372 // lanes is a superset of the lanes we started rendering with.373 //374 // So we'll throw out the current work and restart.375 prepareFreshStack(root, NoLanes);376 } else if (exitStatus !== RootIncomplete) {377 if (exitStatus === RootErrored) {378 executionContext |= RetryAfterError; // If an error occurred during hydration,379 // discard server response and fall back to client side render.380 if (root.hydrate) {381 root.hydrate = false;382 clearContainer(root.containerInfo);383 } // If something threw an error, try rendering one more time. We'll render384 // synchronously to block concurrent data mutations, and we'll includes385 // all pending updates are included. If it still fails after the second386 // attempt, we'll give up and commit the resulting tree.387 lanes = getLanesToRetrySynchronouslyOnError(root);388 if (lanes !== NoLanes) {389 exitStatus = renderRootSync(root, lanes);390 }391 }392 if (exitStatus === RootFatalErrored) {393 var fatalError = workInProgressRootFatalError;394 prepareFreshStack(root, NoLanes);395 markRootSuspended$1(root, lanes);396 ensureRootIsScheduled(root, now());397 throw fatalError;398 } // We now have a consistent tree. The next step is either to commit it,399 // or, if something suspended, wait to commit it after a timeout.400 var finishedWork = root.current.alternate;401 root.finishedWork = finishedWork;402 root.finishedLanes = lanes;403 finishConcurrentRender(root, exitStatus, lanes);404 }405 ensureRootIsScheduled(root, now());406 if (root.callbackNode === originalCallbackNode) {407 // The task node scheduled for this root is the same one that's408 // currently executed. Need to return a continuation.409 return performConcurrentWorkOnRoot.bind(null, root);410 }411 return null;412 }413 function finishConcurrentRender(root, exitStatus, lanes) {414 switch (exitStatus) {415 case RootIncomplete:416 case RootFatalErrored:417 {418 {419 {420 throw Error( "Root did not complete. This is a bug in React." );421 }422 }423 }424 // Flow knows about invariant, so it complains if I add a break425 // statement, but eslint doesn't know about invariant, so it complains426 // if I do. eslint-disable-next-line no-fallthrough427 case RootErrored:428 {429 // We should have already attempted to retry this tree. If we reached430 // this point, it errored again. Commit it.431 commitRoot(root);432 break;433 }434 case RootSuspended:435 {436 markRootSuspended$1(root, lanes); // We have an acceptable loading state. We need to figure out if we437 // should immediately commit it or wait a bit.438 if (includesOnlyRetries(lanes) && // do not delay if we're inside an act() scope439 !shouldForceFlushFallbacksInDEV()) {440 // This render only included retries, no updates. Throttle committing441 // retries so that we don't show too many loading states too quickly.442 var msUntilTimeout = globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now(); // Don't bother with a very short suspense time.443 if (msUntilTimeout > 10) {444 var nextLanes = getNextLanes(root, NoLanes);445 if (nextLanes !== NoLanes) {446 // There's additional work on this root.447 break;448 }449 var suspendedLanes = root.suspendedLanes;450 if (!isSubsetOfLanes(suspendedLanes, lanes)) {451 // We should prefer to render the fallback of at the last452 // suspended level. Ping the last suspended level to try453 // rendering it again.454 // FIXME: What if the suspended lanes are Idle? Should not restart.455 var eventTime = requestEventTime();456 markRootPinged(root, suspendedLanes);457 break;458 } // The render is suspended, it hasn't timed out, and there's no459 // lower priority work to do. Instead of committing the fallback460 // immediately, wait for more data to arrive.461 root.timeoutHandle = scheduleTimeout(commitRoot.bind(null, root), msUntilTimeout);462 break;463 }464 } // The work expired. Commit immediately.465 commitRoot(root);466 break;467 }468 case RootSuspendedWithDelay:469 {470 markRootSuspended$1(root, lanes);471 if (includesOnlyTransitions(lanes)) {472 // This is a transition, so we should exit without committing a473 // placeholder and without scheduling a timeout. Delay indefinitely474 // until we receive more data.475 break;476 }477 if (!shouldForceFlushFallbacksInDEV()) {478 // This is not a transition, but we did trigger an avoided state.479 // Schedule a placeholder to display after a short delay, using the Just480 // Noticeable Difference.481 // TODO: Is the JND optimization worth the added complexity? If this is482 // the only reason we track the event time, then probably not.483 // Consider removing.484 var mostRecentEventTime = getMostRecentEventTime(root, lanes);485 var eventTimeMs = mostRecentEventTime;486 var timeElapsedMs = now() - eventTimeMs;487 var _msUntilTimeout = jnd(timeElapsedMs) - timeElapsedMs; // Don't bother with a very short suspense time.488 if (_msUntilTimeout > 10) {489 // Instead of committing the fallback immediately, wait for more data490 // to arrive.491 root.timeoutHandle = scheduleTimeout(commitRoot.bind(null, root), _msUntilTimeout);492 break;493 }494 } // Commit the placeholder.495 commitRoot(root);496 break;497 }498 case RootCompleted:499 {500 // The work completed. Ready to commit.501 commitRoot(root);502 break;503 }504 default:505 {506 {507 {508 throw Error( "Unknown root exit status." );509 }510 }511 }512 }513 }514 function markRootSuspended$1(root, suspendedLanes) {515 // When suspending, we should always exclude lanes that were pinged or (more516 // rarely, since we try to avoid it) updated during the render phase.517 // TODO: Lol maybe there's a better way to factor this besides this518 // obnoxiously named function :)519 suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);520 suspendedLanes = removeLanes(suspendedLanes, workInProgressRootUpdatedLanes);521 markRootSuspended(root, suspendedLanes);522 } // This is the entry point for synchronous tasks that don't go523 // through Scheduler524 function performSyncWorkOnRoot(root) {525 if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) {526 {527 throw Error( "Should not already be working." );528 }529 }530 flushPassiveEffects();531 var lanes;532 var exitStatus;533 if (root === workInProgressRoot && includesSomeLane(root.expiredLanes, workInProgressRootRenderLanes)) {534 // There's a partial tree, and at least one of its lanes has expired. Finish535 // rendering it before rendering the rest of the expired work.536 lanes = workInProgressRootRenderLanes;537 exitStatus = renderRootSync(root, lanes);538 if (includesSomeLane(workInProgressRootIncludedLanes, workInProgressRootUpdatedLanes)) {539 // The render included lanes that were updated during the render phase.540 // For example, when unhiding a hidden tree, we include all the lanes541 // that were previously skipped when the tree was hidden. That set of542 // lanes is a superset of the lanes we started rendering with.543 //544 // Note that this only happens when part of the tree is rendered545 // concurrently. If the whole tree is rendered synchronously, then there546 // are no interleaved events.547 lanes = getNextLanes(root, lanes);548 exitStatus = renderRootSync(root, lanes);549 }550 } else {551 lanes = getNextLanes(root, NoLanes);552 exitStatus = renderRootSync(root, lanes);553 }554 if (root.tag !== LegacyRoot && exitStatus === RootErrored) {555 executionContext |= RetryAfterError; // If an error occurred during hydration,556 // discard server response and fall back to client side render.557 if (root.hydrate) {558 root.hydrate = false;559 clearContainer(root.containerInfo);560 } // If something threw an error, try rendering one more time. We'll render561 // synchronously to block concurrent data mutations, and we'll includes562 // all pending updates are included. If it still fails after the second563 // attempt, we'll give up and commit the resulting tree.564 lanes = getLanesToRetrySynchronouslyOnError(root);565 if (lanes !== NoLanes) {566 exitStatus = renderRootSync(root, lanes);567 }568 }569 if (exitStatus === RootFatalErrored) {570 var fatalError = workInProgressRootFatalError;571 prepareFreshStack(root, NoLanes);572 markRootSuspended$1(root, lanes);573 ensureRootIsScheduled(root, now());574 throw fatalError;575 } // We now have a consistent tree. Because this is a sync render, we576 // will commit it even if something suspended.577 var finishedWork = root.current.alternate;578 root.finishedWork = finishedWork;579 root.finishedLanes = lanes;580 commitRoot(root); // Before exiting, make sure there's a callback scheduled for the next581 // pending level.582 ensureRootIsScheduled(root, now());583 return null;584 }585 function flushRoot(root, lanes) {586 markRootExpired(root, lanes);587 ensureRootIsScheduled(root, now());588 if ((executionContext & (RenderContext | CommitContext)) === NoContext) {589 resetRenderTimer();590 flushSyncCallbackQueue();591 }592 }593 function getExecutionContext() {594 return executionContext;595 }596 // flush reactäºä»¶597 function flushDiscreteUpdates() {598 // TODO: Should be able to flush inside batchedUpdates, but not inside `act`.599 // However, `act` uses `batchedUpdates`, so there's no way to distinguish600 // those two cases. Need to fix this before exposing flushDiscreteUpdates601 // as a public API.602 if ((executionContext & (BatchedContext | RenderContext | CommitContext)) !== NoContext) {603 {604 if ((executionContext & RenderContext) !== NoContext) {605 error('unstable_flushDiscreteUpdates: Cannot flush updates when React is ' + 'already rendering.');606 }607 } // We're already rendering, so we can't synchronously flush pending work.608 // This is probably a nested event dispatch triggered by a lifecycle/effect,609 // like `el.focus()`. Exit.610 return;611 }612 flushPendingDiscreteUpdates(); // If the discrete updates scheduled passive effects, flush them now so that613 // they fire before the next serial event.614 flushPassiveEffects();615 }616 function flushPendingDiscreteUpdates() {617 if (rootsWithPendingDiscreteUpdates !== null) {618 // For each root with pending discrete updates, schedule a callback to619 // immediately flush them.620 var roots = rootsWithPendingDiscreteUpdates;621 rootsWithPendingDiscreteUpdates = null;622 roots.forEach(function (root) {623 markDiscreteUpdatesExpired(root);624 ensureRootIsScheduled(root, now());625 });626 } // Now flush the immediate queue.627 flushSyncCallbackQueue();628 }629 function batchedUpdates$1(fn, a) {630 var prevExecutionContext = executionContext;631 executionContext |= BatchedContext;632 try {633 return fn(a);634 } finally {635 executionContext = prevExecutionContext;636 if (executionContext === NoContext) {637 // Flush the immediate callbacks that were scheduled during this batch638 resetRenderTimer();639 flushSyncCallbackQueue();640 }641 }642 }643 function batchedEventUpdates$1(fn, a) {644 var prevExecutionContext = executionContext;645 executionContext |= EventContext;646 try {647 return fn(a);648 } finally {649 executionContext = prevExecutionContext;650 if (executionContext === NoContext) {651 // Flush the immediate callbacks that were scheduled during this batch652 resetRenderTimer();653 flushSyncCallbackQueue();654 }655 }656 }657 function discreteUpdates$1(fn, a, b, c, d) {658 var prevExecutionContext = executionContext;659 executionContext |= DiscreteEventContext;660 {661 try {662 return runWithPriority$1(UserBlockingPriority$2, fn.bind(null, a, b, c, d));663 } finally {664 executionContext = prevExecutionContext;665 if (executionContext === NoContext) {666 // Flush the immediate callbacks that were scheduled during this batch667 resetRenderTimer();668 flushSyncCallbackQueue();669 }670 }671 }672 }673 // éæ¹éæ´æ°674 function unbatchedUpdates(fn, a) {675 var prevExecutionContext = executionContext;676 executionContext &= ~BatchedContext;677 executionContext |= LegacyUnbatchedContext;678 try {679 return fn(a);680 } finally {681 executionContext = prevExecutionContext;682 if (executionContext === NoContext) {683 // Flush the immediate callbacks that were scheduled during this batch684 resetRenderTimer();685 flushSyncCallbackQueue();686 }687 }688 }689 function flushSync(fn, a) {690 var prevExecutionContext = executionContext;691 if ((prevExecutionContext & (RenderContext | CommitContext)) !== NoContext) {692 {693 error('flushSync was called from inside a lifecycle method. React cannot ' + 'flush when React is already rendering. Consider moving this call to ' + 'a scheduler task or micro task.');694 }695 return fn(a);696 }697 executionContext |= BatchedContext;698 {699 try {700 if (fn) {701 return runWithPriority$1(ImmediatePriority$1, fn.bind(null, a));702 } else {703 return undefined;704 }705 } finally {706 executionContext = prevExecutionContext; // Flush the immediate callbacks that were scheduled during this batch.707 // Note that this will happen even if batchedUpdates is higher up708 // the stack.709 flushSyncCallbackQueue();710 }711 }712 }713 function flushControlled(fn) {714 var prevExecutionContext = executionContext;715 executionContext |= BatchedContext;716 {717 try {718 runWithPriority$1(ImmediatePriority$1, fn);719 } finally {720 executionContext = prevExecutionContext;721 if (executionContext === NoContext) {722 // Flush the immediate callbacks that were scheduled during this batch723 resetRenderTimer();724 flushSyncCallbackQueue();725 }726 }727 }728 }729 function pushRenderLanes(fiber, lanes) {730 push(subtreeRenderLanesCursor, subtreeRenderLanes, fiber);731 subtreeRenderLanes = mergeLanes(subtreeRenderLanes, lanes);732 workInProgressRootIncludedLanes = mergeLanes(workInProgressRootIncludedLanes, lanes);733 }734 function popRenderLanes(fiber) {735 subtreeRenderLanes = subtreeRenderLanesCursor.current;736 pop(subtreeRenderLanesCursor, fiber);737 }738 function prepareFreshStack(root, lanes) {739 root.finishedWork = null;740 root.finishedLanes = NoLanes;741 var timeoutHandle = root.timeoutHandle;742 if (timeoutHandle !== noTimeout) {743 // The root previous suspended and scheduled a timeout to commit a fallback744 // state. Now that we have additional work, cancel the timeout.745 root.timeoutHandle = noTimeout; // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above746 cancelTimeout(timeoutHandle);747 }748 if (workInProgress !== null) {749 var interruptedWork = workInProgress.return;750 while (interruptedWork !== null) {751 unwindInterruptedWork(interruptedWork);752 interruptedWork = interruptedWork.return;753 }754 }755 workInProgressRoot = root;756 workInProgress = createWorkInProgress(root.current, null);757 workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;758 workInProgressRootExitStatus = RootIncomplete;759 workInProgressRootFatalError = null;760 workInProgressRootSkippedLanes = NoLanes;761 workInProgressRootUpdatedLanes = NoLanes;762 workInProgressRootPingedLanes = NoLanes;763 {764 spawnedWorkDuringRender = null;765 }766 {767 ReactStrictModeWarnings.discardPendingWarnings();768 }769 }770 function handleError(root, thrownValue) {771 do {772 var erroredWork = workInProgress;773 try {774 // Reset module-level state that was set during the render phase.775 resetContextDependencies();776 resetHooksAfterThrow();777 resetCurrentFiber(); // TODO: I found and added this missing line while investigating a778 // separate issue. Write a regression test using string refs.779 ReactCurrentOwner$2.current = null;780 if (erroredWork === null || erroredWork.return === null) {781 // Expected to be working on a non-root fiber. This is a fatal error782 // because there's no ancestor that can handle it; the root is783 // supposed to capture all errors that weren't caught by an error784 // boundary.785 workInProgressRootExitStatus = RootFatalErrored;786 workInProgressRootFatalError = thrownValue; // Set `workInProgress` to null. This represents advancing to the next787 // sibling, or the parent if there are no siblings. But since the root788 // has no siblings nor a parent, we set it to null. Usually this is789 // handled by `completeUnitOfWork` or `unwindWork`, but since we're790 // intentionally not calling those, we need set it here.791 // TODO: Consider calling `unwindWork` to pop the contexts.792 workInProgress = null;793 return;794 }795 if (enableProfilerTimer && erroredWork.mode & ProfileMode) {796 // Record the time spent rendering before an error was thrown. This797 // avoids inaccurate Profiler durations in the case of a798 // suspended render.799 stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true);800 }801 throwException(root, erroredWork.return, erroredWork, thrownValue, workInProgressRootRenderLanes);802 completeUnitOfWork(erroredWork);803 } catch (yetAnotherThrownValue) {804 // Something in the return path also threw.805 thrownValue = yetAnotherThrownValue;806 if (workInProgress === erroredWork && erroredWork !== null) {807 // If this boundary has already errored, then we had trouble processing808 // the error. Bubble it to the next boundary.809 erroredWork = erroredWork.return;810 workInProgress = erroredWork;811 } else {812 erroredWork = workInProgress;813 }814 continue;815 } // Return to the normal work loop.816 return;817 } while (true);818 }819 function pushDispatcher() {820 var prevDispatcher = ReactCurrentDispatcher$2.current;821 ReactCurrentDispatcher$2.current = ContextOnlyDispatcher;822 if (prevDispatcher === null) {823 // The React isomorphic package does not include a default dispatcher.824 // Instead the first renderer will lazily attach one, in order to give825 // nicer error messages.826 return ContextOnlyDispatcher;827 } else {828 return prevDispatcher;829 }830 }831 function popDispatcher(prevDispatcher) {832 ReactCurrentDispatcher$2.current = prevDispatcher;833 }834 function pushInteractions(root) {835 {836 var prevInteractions = __interactionsRef.current;837 __interactionsRef.current = root.memoizedInteractions;838 return prevInteractions;839 }840 }841 function popInteractions(prevInteractions) {842 {843 __interactionsRef.current = prevInteractions;844 }845 }846 function markCommitTimeOfFallback() {847 globalMostRecentFallbackTime = now();848 }849 function markSkippedUpdateLanes(lane) {850 workInProgressRootSkippedLanes = mergeLanes(lane, workInProgressRootSkippedLanes);851 }852 function renderDidSuspend() {853 if (workInProgressRootExitStatus === RootIncomplete) {854 workInProgressRootExitStatus = RootSuspended;855 }856 }857 function renderDidSuspendDelayIfPossible() {858 if (workInProgressRootExitStatus === RootIncomplete || workInProgressRootExitStatus === RootSuspended) {859 workInProgressRootExitStatus = RootSuspendedWithDelay;860 } // Check if there are updates that we skipped tree that might have unblocked861 // this render.862 if (workInProgressRoot !== null && (includesNonIdleWork(workInProgressRootSkippedLanes) || includesNonIdleWork(workInProgressRootUpdatedLanes))) {863 // Mark the current render as suspended so that we switch to working on864 // the updates that were skipped. Usually we only suspend at the end of865 // the render phase.866 // TODO: We should probably always mark the root as suspended immediately867 // (inside this function), since by suspending at the end of the render868 // phase introduces a potential mistake where we suspend lanes that were869 // pinged or updated while we were rendering.870 markRootSuspended$1(workInProgressRoot, workInProgressRootRenderLanes);871 }872 }873 function renderDidError() {874 if (workInProgressRootExitStatus !== RootCompleted) {875 workInProgressRootExitStatus = RootErrored;876 }877 } // Called during render to determine if anything has suspended.878 // Returns false if we're not sure.879 function renderHasNotSuspendedYet() {880 // If something errored or completed, we can't really be sure,881 // so those are false.882 return workInProgressRootExitStatus === RootIncomplete;883 }884 function renderRootSync(root, lanes) {885 var prevExecutionContext = executionContext;886 executionContext |= RenderContext;887 var prevDispatcher = pushDispatcher(); // If the root or lanes have changed, throw out the existing stack888 // and prepare a fresh one. Otherwise we'll continue where we left off.889 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {890 prepareFreshStack(root, lanes);891 startWorkOnPendingInteractions(root, lanes);892 }893 var prevInteractions = pushInteractions(root);894 {895 markRenderStarted(lanes);896 }897 do {898 try {899 workLoopSync();900 break;901 } catch (thrownValue) {902 handleError(root, thrownValue);903 }904 } while (true);905 resetContextDependencies();906 {907 popInteractions(prevInteractions);908 }909 executionContext = prevExecutionContext;910 popDispatcher(prevDispatcher);911 if (workInProgress !== null) {912 // This is a sync render, so we should have finished the whole tree.913 {914 {915 throw Error( "Cannot commit an incomplete root. This error is likely caused by a bug in React. Please file an issue." );916 }917 }918 }919 {920 markRenderStopped();921 } // Set this to null to indicate there's no in-progress render.922 workInProgressRoot = null;923 workInProgressRootRenderLanes = NoLanes;924 return workInProgressRootExitStatus;925 } // The work loop is an extremely hot path. Tell Closure not to inline it.926 /** @noinline */927 function workLoopSync() {928 // Already timed out, so perform work without checking if we need to yield.929 while (workInProgress !== null) {930 performUnitOfWork(workInProgress);931 }932 }933 function renderRootConcurrent(root, lanes) {934 var prevExecutionContext = executionContext;935 executionContext |= RenderContext;936 var prevDispatcher = pushDispatcher(); // If the root or lanes have changed, throw out the existing stack937 // and prepare a fresh one. Otherwise we'll continue where we left off.938 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {939 resetRenderTimer();940 prepareFreshStack(root, lanes);941 startWorkOnPendingInteractions(root, lanes);942 }943 var prevInteractions = pushInteractions(root);944 {945 markRenderStarted(lanes);946 }947 do {948 try {949 workLoopConcurrent();950 break;951 } catch (thrownValue) {952 handleError(root, thrownValue);953 }954 } while (true);955 resetContextDependencies();956 {957 popInteractions(prevInteractions);958 }959 popDispatcher(prevDispatcher);960 executionContext = prevExecutionContext;961 if (workInProgress !== null) {962 // Still work remaining.963 {964 markRenderYielded();965 }966 return RootIncomplete;967 } else {968 // Completed the tree.969 {970 markRenderStopped();971 } // Set this to null to indicate there's no in-progress render.972 workInProgressRoot = null;973 workInProgressRootRenderLanes = NoLanes; // Return the final exit status.974 return workInProgressRootExitStatus;975 }976 }977 /** @noinline */978 function workLoopConcurrent() {979 // Perform work until Scheduler asks us to yield980 while (workInProgress !== null && !shouldYield()) {981 performUnitOfWork(workInProgress);982 }983 }984 function performUnitOfWork(unitOfWork) {985 // The current, flushed, state of this fiber is the alternate. Ideally986 // nothing should rely on this, but relying on it here means that we don't987 // need an additional field on the work in progress.988 var current = unitOfWork.alternate;989 setCurrentFiber(unitOfWork);990 var next;991 if ( (unitOfWork.mode & ProfileMode) !== NoMode) {992 startProfilerTimer(unitOfWork);993 next = beginWork$1(current, unitOfWork, subtreeRenderLanes);994 stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);995 } else {996 next = beginWork$1(current, unitOfWork, subtreeRenderLanes);997 }998 resetCurrentFiber();999 unitOfWork.memoizedProps = unitOfWork.pendingProps;1000 if (next === null) {1001 // If this doesn't spawn new work, complete the current work.1002 completeUnitOfWork(unitOfWork);1003 } else {1004 workInProgress = next;1005 }1006 ReactCurrentOwner$2.current = null;1007 }1008 function completeUnitOfWork(unitOfWork) {1009 // Attempt to complete the current unit of work, then move to the next1010 // sibling. If there are no more siblings, return to the parent fiber.1011 var completedWork = unitOfWork;1012 do {1013 // The current, flushed, state of this fiber is the alternate. Ideally1014 // nothing should rely on this, but relying on it here means that we don't1015 // need an additional field on the work in progress.1016 var current = completedWork.alternate;1017 var returnFiber = completedWork.return; // Check if the work completed or if something threw.1018 if ((completedWork.flags & Incomplete) === NoFlags) {1019 setCurrentFiber(completedWork);1020 var next = void 0;1021 if ( (completedWork.mode & ProfileMode) === NoMode) {1022 next = completeWork(current, completedWork, subtreeRenderLanes);1023 } else {1024 startProfilerTimer(completedWork);1025 next = completeWork(current, completedWork, subtreeRenderLanes); // Update render duration assuming we didn't error.1026 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1027 }1028 resetCurrentFiber();1029 if (next !== null) {1030 // Completing this fiber spawned new work. Work on that next.1031 workInProgress = next;1032 return;1033 }1034 resetChildLanes(completedWork);1035 if (returnFiber !== null && // Do not append effects to parents if a sibling failed to complete1036 (returnFiber.flags & Incomplete) === NoFlags) {1037 // Append all the effects of the subtree and this fiber onto the effect1038 // list of the parent. The completion order of the children affects the1039 // side-effect order.1040 if (returnFiber.firstEffect === null) {1041 returnFiber.firstEffect = completedWork.firstEffect;1042 }1043 if (completedWork.lastEffect !== null) {1044 if (returnFiber.lastEffect !== null) {1045 returnFiber.lastEffect.nextEffect = completedWork.firstEffect;1046 }1047 returnFiber.lastEffect = completedWork.lastEffect;1048 } // If this fiber had side-effects, we append it AFTER the children's1049 // side-effects. We can perform certain side-effects earlier if needed,1050 // by doing multiple passes over the effect list. We don't want to1051 // schedule our own side-effect on our own list because if end up1052 // reusing children we'll schedule this effect onto itself since we're1053 // at the end.1054 var flags = completedWork.flags; // Skip both NoWork and PerformedWork tags when creating the effect1055 // list. PerformedWork effect is read by React DevTools but shouldn't be1056 // committed.1057 if (flags > PerformedWork) {1058 if (returnFiber.lastEffect !== null) {1059 returnFiber.lastEffect.nextEffect = completedWork;1060 } else {1061 returnFiber.firstEffect = completedWork;1062 }1063 returnFiber.lastEffect = completedWork;1064 }1065 }1066 } else {1067 // This fiber did not complete because something threw. Pop values off1068 // the stack without entering the complete phase. If this is a boundary,1069 // capture values if possible.1070 var _next = unwindWork(completedWork); // Because this fiber did not complete, don't reset its expiration time.1071 if (_next !== null) {1072 // If completing this work spawned new work, do that next. We'll come1073 // back here again.1074 // Since we're restarting, remove anything that is not a host effect1075 // from the effect tag.1076 _next.flags &= HostEffectMask;1077 workInProgress = _next;1078 return;1079 }1080 if ( (completedWork.mode & ProfileMode) !== NoMode) {1081 // Record the render duration for the fiber that errored.1082 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false); // Include the time spent working on failed children before continuing.1083 var actualDuration = completedWork.actualDuration;1084 var child = completedWork.child;1085 while (child !== null) {1086 actualDuration += child.actualDuration;1087 child = child.sibling;1088 }1089 completedWork.actualDuration = actualDuration;1090 }1091 if (returnFiber !== null) {1092 // Mark the parent fiber as incomplete and clear its effect list.1093 returnFiber.firstEffect = returnFiber.lastEffect = null;1094 returnFiber.flags |= Incomplete;1095 }1096 }1097 var siblingFiber = completedWork.sibling;1098 if (siblingFiber !== null) {1099 // If there is more work to do in this returnFiber, do that next.1100 workInProgress = siblingFiber;1101 return;1102 } // Otherwise, return to the parent1103 completedWork = returnFiber; // Update the next thing we're working on in case something throws.1104 workInProgress = completedWork;1105 } while (completedWork !== null); // We've reached the root.1106 if (workInProgressRootExitStatus === RootIncomplete) {1107 workInProgressRootExitStatus = RootCompleted;1108 }1109 }1110 function resetChildLanes(completedWork) {1111 if ( // TODO: Move this check out of the hot path by moving `resetChildLanes`1112 // to switch statement in `completeWork`.1113 (completedWork.tag === LegacyHiddenComponent || completedWork.tag === OffscreenComponent) && completedWork.memoizedState !== null && !includesSomeLane(subtreeRenderLanes, OffscreenLane) && (completedWork.mode & ConcurrentMode) !== NoLanes) {1114 // The children of this component are hidden. Don't bubble their1115 // expiration times.1116 return;1117 }1118 var newChildLanes = NoLanes; // Bubble up the earliest expiration time.1119 if ( (completedWork.mode & ProfileMode) !== NoMode) {1120 // In profiling mode, resetChildExpirationTime is also used to reset1121 // profiler durations.1122 var actualDuration = completedWork.actualDuration;1123 var treeBaseDuration = completedWork.selfBaseDuration; // When a fiber is cloned, its actualDuration is reset to 0. This value will1124 // only be updated if work is done on the fiber (i.e. it doesn't bailout).1125 // When work is done, it should bubble to the parent's actualDuration. If1126 // the fiber has not been cloned though, (meaning no work was done), then1127 // this value will reflect the amount of time spent working on a previous1128 // render. In that case it should not bubble. We determine whether it was1129 // cloned by comparing the child pointer.1130 var shouldBubbleActualDurations = completedWork.alternate === null || completedWork.child !== completedWork.alternate.child;1131 var child = completedWork.child;1132 while (child !== null) {1133 newChildLanes = mergeLanes(newChildLanes, mergeLanes(child.lanes, child.childLanes));1134 if (shouldBubbleActualDurations) {1135 actualDuration += child.actualDuration;1136 }1137 treeBaseDuration += child.treeBaseDuration;1138 child = child.sibling;1139 }1140 var isTimedOutSuspense = completedWork.tag === SuspenseComponent && completedWork.memoizedState !== null;1141 if (isTimedOutSuspense) {1142 // Don't count time spent in a timed out Suspense subtree as part of the base duration.1143 var primaryChildFragment = completedWork.child;1144 if (primaryChildFragment !== null) {1145 treeBaseDuration -= primaryChildFragment.treeBaseDuration;1146 }1147 }1148 completedWork.actualDuration = actualDuration;1149 completedWork.treeBaseDuration = treeBaseDuration;1150 } else {1151 var _child = completedWork.child;1152 while (_child !== null) {1153 newChildLanes = mergeLanes(newChildLanes, mergeLanes(_child.lanes, _child.childLanes));1154 _child = _child.sibling;1155 }1156 }1157 completedWork.childLanes = newChildLanes;1158 }1159 function commitRoot(root) {1160 var renderPriorityLevel = getCurrentPriorityLevel();1161 runWithPriority$1(ImmediatePriority$1, commitRootImpl.bind(null, root, renderPriorityLevel));1162 return null;1163 }1164 function commitRootImpl(root, renderPriorityLevel) {1165 do {1166 // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which1167 // means `flushPassiveEffects` will sometimes result in additional1168 // passive effects. So we need to keep flushing in a loop until there are1169 // no more pending effects.1170 // TODO: Might be better if `flushPassiveEffects` did not automatically1171 // flush synchronous work at the end, to avoid factoring hazards like this.1172 flushPassiveEffects();1173 } while (rootWithPendingPassiveEffects !== null);1174 flushRenderPhaseStrictModeWarningsInDEV();1175 if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) {1176 {1177 throw Error( "Should not already be working." );1178 }1179 }1180 var finishedWork = root.finishedWork;1181 var lanes = root.finishedLanes;1182 {1183 markCommitStarted(lanes);1184 }1185 if (finishedWork === null) {1186 {1187 markCommitStopped();1188 }1189 return null;1190 }1191 root.finishedWork = null;1192 root.finishedLanes = NoLanes;1193 if (!(finishedWork !== root.current)) {1194 {1195 throw Error( "Cannot commit the same tree as before. This error is likely caused by a bug in React. Please file an issue." );1196 }1197 } // commitRoot never returns a continuation; it always finishes synchronously.1198 // So we can clear these now to allow a new callback to be scheduled.1199 root.callbackNode = null; // Update the first and last pending times on this root. The new first1200 // pending time is whatever is left on the root fiber.1201 var remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);1202 markRootFinished(root, remainingLanes); // Clear already finished discrete updates in case that a later call of1203 // `flushDiscreteUpdates` starts a useless render pass which may cancels1204 // a scheduled timeout.1205 if (rootsWithPendingDiscreteUpdates !== null) {1206 if (!hasDiscreteLanes(remainingLanes) && rootsWithPendingDiscreteUpdates.has(root)) {1207 rootsWithPendingDiscreteUpdates.delete(root);1208 }1209 }1210 if (root === workInProgressRoot) {1211 // We can reset these now that they are finished.1212 workInProgressRoot = null;1213 workInProgress = null;1214 workInProgressRootRenderLanes = NoLanes;1215 } // Get the list of effects.1216 var firstEffect;1217 if (finishedWork.flags > PerformedWork) {1218 // A fiber's effect list consists only of its children, not itself. So if1219 // the root has an effect, we need to add it to the end of the list. The1220 // resulting list is the set that would belong to the root's parent, if it1221 // had one; that is, all the effects in the tree including the root.1222 if (finishedWork.lastEffect !== null) {1223 finishedWork.lastEffect.nextEffect = finishedWork;1224 firstEffect = finishedWork.firstEffect;1225 } else {1226 firstEffect = finishedWork;1227 }1228 } else {1229 // There is no effect on the root.1230 firstEffect = finishedWork.firstEffect;1231 }1232 if (firstEffect !== null) {1233 var prevExecutionContext = executionContext;1234 executionContext |= CommitContext;1235 var prevInteractions = pushInteractions(root); // Reset this to null before calling lifecycles1236 ReactCurrentOwner$2.current = null; // The commit phase is broken into several sub-phases. We do a separate pass1237 // of the effect list for each phase: all mutation effects come before all1238 // layout effects, and so on.1239 // The first phase a "before mutation" phase. We use this phase to read the1240 // state of the host tree right before we mutate it. This is where1241 // getSnapshotBeforeUpdate is called.1242 focusedInstanceHandle = prepareForCommit(root.containerInfo);1243 shouldFireAfterActiveInstanceBlur = false;1244 nextEffect = firstEffect;1245 do {1246 {1247 invokeGuardedCallback(null, commitBeforeMutationEffects, null);1248 if (hasCaughtError()) {1249 if (!(nextEffect !== null)) {1250 {1251 throw Error( "Should be working on an effect." );1252 }1253 }1254 var error = clearCaughtError();1255 captureCommitPhaseError(nextEffect, error);1256 nextEffect = nextEffect.nextEffect;1257 }1258 }1259 } while (nextEffect !== null); // We no longer need to track the active instance fiber1260 focusedInstanceHandle = null;1261 {1262 // Mark the current commit time to be shared by all Profilers in this1263 // batch. This enables them to be grouped later.1264 recordCommitTime();1265 } // The next phase is the mutation phase, where we mutate the host tree.1266 nextEffect = firstEffect;1267 do {1268 {1269 invokeGuardedCallback(null, commitMutationEffects, null, root, renderPriorityLevel);1270 if (hasCaughtError()) {1271 if (!(nextEffect !== null)) {1272 {1273 throw Error( "Should be working on an effect." );1274 }1275 }1276 var _error = clearCaughtError();1277 captureCommitPhaseError(nextEffect, _error);1278 nextEffect = nextEffect.nextEffect;1279 }1280 }1281 } while (nextEffect !== null);1282 resetAfterCommit(root.containerInfo); // The work-in-progress tree is now the current tree. This must come after1283 // the mutation phase, so that the previous tree is still current during1284 // componentWillUnmount, but before the layout phase, so that the finished1285 // work is current during componentDidMount/Update.1286 root.current = finishedWork; // The next phase is the layout phase, where we call effects that read1287 // the host tree after it's been mutated. The idiomatic use case for this is1288 // layout, but class component lifecycles also fire here for legacy reasons.1289 nextEffect = firstEffect;1290 do {1291 {1292 invokeGuardedCallback(null, commitLayoutEffects, null, root, lanes);1293 if (hasCaughtError()) {1294 if (!(nextEffect !== null)) {1295 {1296 throw Error( "Should be working on an effect." );1297 }1298 }1299 var _error2 = clearCaughtError();1300 captureCommitPhaseError(nextEffect, _error2);1301 nextEffect = nextEffect.nextEffect;1302 }1303 }1304 } while (nextEffect !== null);1305 nextEffect = null; // Tell Scheduler to yield at the end of the frame, so the browser has an1306 // opportunity to paint.1307 requestPaint();1308 {1309 popInteractions(prevInteractions);1310 }1311 executionContext = prevExecutionContext;1312 } else {1313 // No effects.1314 root.current = finishedWork; // Measure these anyway so the flamegraph explicitly shows that there were1315 // no effects.1316 // TODO: Maybe there's a better way to report this.1317 {1318 recordCommitTime();1319 }1320 }1321 var rootDidHavePassiveEffects = rootDoesHavePassiveEffects;1322 if (rootDoesHavePassiveEffects) {1323 // This commit has passive effects. Stash a reference to them. But don't1324 // schedule a callback until after flushing layout work.1325 rootDoesHavePassiveEffects = false;1326 rootWithPendingPassiveEffects = root;1327 pendingPassiveEffectsLanes = lanes;1328 pendingPassiveEffectsRenderPriority = renderPriorityLevel;1329 } else {1330 // We are done with the effect chain at this point so let's clear the1331 // nextEffect pointers to assist with GC. If we have passive effects, we'll1332 // clear this in flushPassiveEffects.1333 nextEffect = firstEffect;1334 while (nextEffect !== null) {1335 var nextNextEffect = nextEffect.nextEffect;1336 nextEffect.nextEffect = null;1337 if (nextEffect.flags & Deletion) {1338 detachFiberAfterEffects(nextEffect);1339 }1340 nextEffect = nextNextEffect;1341 }1342 } // Read this again, since an effect might have updated it1343 remainingLanes = root.pendingLanes; // Check if there's remaining work on this root1344 if (remainingLanes !== NoLanes) {1345 {1346 if (spawnedWorkDuringRender !== null) {1347 var expirationTimes = spawnedWorkDuringRender;1348 spawnedWorkDuringRender = null;1349 for (var i = 0; i < expirationTimes.length; i++) {1350 scheduleInteractions(root, expirationTimes[i], root.memoizedInteractions);1351 }1352 }1353 schedulePendingInteractions(root, remainingLanes);1354 }1355 } else {1356 // If there's no remaining work, we can clear the set of already failed1357 // error boundaries.1358 legacyErrorBoundariesThatAlreadyFailed = null;1359 }1360 {1361 if (!rootDidHavePassiveEffects) {1362 // If there are no passive effects, then we can complete the pending interactions.1363 // Otherwise, we'll wait until after the passive effects are flushed.1364 // Wait to do this until after remaining work has been scheduled,1365 // so that we don't prematurely signal complete for interactions when there's e.g. hidden work.1366 finishPendingInteractions(root, lanes);1367 }1368 }1369 if (remainingLanes === SyncLane) {1370 // Count the number of times the root synchronously re-renders without1371 // finishing. If there are too many, it indicates an infinite update loop.1372 if (root === rootWithNestedUpdates) {1373 nestedUpdateCount++;1374 } else {1375 nestedUpdateCount = 0;1376 rootWithNestedUpdates = root;1377 }1378 } else {1379 nestedUpdateCount = 0;1380 }1381 onCommitRoot(finishedWork.stateNode, renderPriorityLevel);1382 {1383 onCommitRoot$1();1384 } // Always call this before exiting `commitRoot`, to ensure that any1385 // additional work on this root is scheduled.1386 ensureRootIsScheduled(root, now());1387 if (hasUncaughtError) {1388 hasUncaughtError = false;1389 var _error3 = firstUncaughtError;1390 firstUncaughtError = null;1391 throw _error3;1392 }1393 if ((executionContext & LegacyUnbatchedContext) !== NoContext) {1394 {1395 markCommitStopped();1396 } // This is a legacy edge case. We just committed the initial mount of1397 // a ReactDOM.render-ed root inside of batchedUpdates. The commit fired1398 // synchronously, but layout updates should be deferred until the end1399 // of the batch.1400 return null;1401 } // If layout work was scheduled, flush it now.1402 flushSyncCallbackQueue();1403 {1404 markCommitStopped();1405 }1406 return null;1407 }1408 function commitBeforeMutationEffects() {1409 while (nextEffect !== null) {1410 var current = nextEffect.alternate;1411 if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {1412 if ((nextEffect.flags & Deletion) !== NoFlags) {1413 if (doesFiberContain(nextEffect, focusedInstanceHandle)) {1414 shouldFireAfterActiveInstanceBlur = true;1415 }1416 } else {1417 // TODO: Move this out of the hot path using a dedicated effect tag.1418 if (nextEffect.tag === SuspenseComponent && isSuspenseBoundaryBeingHidden(current, nextEffect) && doesFiberContain(nextEffect, focusedInstanceHandle)) {1419 shouldFireAfterActiveInstanceBlur = true;1420 }1421 }1422 }1423 var flags = nextEffect.flags;1424 if ((flags & Snapshot) !== NoFlags) {1425 setCurrentFiber(nextEffect);1426 commitBeforeMutationLifeCycles(current, nextEffect);1427 resetCurrentFiber();1428 }1429 if ((flags & Passive) !== NoFlags) {1430 // If there are passive effects, schedule a callback to flush at1431 // the earliest opportunity.1432 if (!rootDoesHavePassiveEffects) {1433 rootDoesHavePassiveEffects = true;1434 scheduleCallback(NormalPriority$1, function () {1435 flushPassiveEffects();1436 return null;1437 });1438 }1439 }1440 nextEffect = nextEffect.nextEffect;1441 }1442 }1443 function commitMutationEffects(root, renderPriorityLevel) {1444 // TODO: Should probably move the bulk of this function to commitWork.1445 while (nextEffect !== null) {1446 setCurrentFiber(nextEffect);1447 var flags = nextEffect.flags;1448 if (flags & ContentReset) {1449 commitResetTextContent(nextEffect);1450 }1451 if (flags & Ref) {1452 var current = nextEffect.alternate;1453 if (current !== null) {1454 commitDetachRef(current);1455 }1456 } // The following switch statement is only concerned about placement,1457 // updates, and deletions. To avoid needing to add a case for every possible1458 // bitmap value, we remove the secondary effects from the effect tag and1459 // switch on that value.1460 var primaryFlags = flags & (Placement | Update | Deletion | Hydrating);1461 switch (primaryFlags) {1462 case Placement:1463 {1464 commitPlacement(nextEffect); // Clear the "placement" from effect tag so that we know that this is1465 // inserted, before any life-cycles like componentDidMount gets called.1466 // TODO: findDOMNode doesn't rely on this any more but isMounted does1467 // and isMounted is deprecated anyway so we should be able to kill this.1468 nextEffect.flags &= ~Placement;1469 break;1470 }1471 case PlacementAndUpdate:1472 {1473 // Placement1474 commitPlacement(nextEffect); // Clear the "placement" from effect tag so that we know that this is1475 // inserted, before any life-cycles like componentDidMount gets called.1476 nextEffect.flags &= ~Placement; // Update1477 var _current = nextEffect.alternate;1478 commitWork(_current, nextEffect);1479 break;1480 }1481 case Hydrating:1482 {1483 nextEffect.flags &= ~Hydrating;1484 break;1485 }1486 case HydratingAndUpdate:1487 {1488 nextEffect.flags &= ~Hydrating; // Update1489 var _current2 = nextEffect.alternate;1490 commitWork(_current2, nextEffect);1491 break;1492 }1493 case Update:1494 {1495 var _current3 = nextEffect.alternate;1496 commitWork(_current3, nextEffect);1497 break;1498 }1499 case Deletion:1500 {1501 commitDeletion(root, nextEffect);1502 break;1503 }1504 }1505 resetCurrentFiber();1506 nextEffect = nextEffect.nextEffect;1507 }1508 }1509 function commitLayoutEffects(root, committedLanes) {1510 {1511 markLayoutEffectsStarted(committedLanes);1512 } // TODO: Should probably move the bulk of this function to commitWork.1513 while (nextEffect !== null) {1514 setCurrentFiber(nextEffect);1515 var flags = nextEffect.flags;1516 if (flags & (Update | Callback)) {1517 var current = nextEffect.alternate;1518 commitLifeCycles(root, current, nextEffect);1519 }1520 {1521 if (flags & Ref) {1522 commitAttachRef(nextEffect);1523 }1524 }1525 resetCurrentFiber();1526 nextEffect = nextEffect.nextEffect;1527 }1528 {1529 markLayoutEffectsStopped();1530 }1531 }1532 function flushPassiveEffects() {1533 // Returns whether passive effects were flushed.1534 if (pendingPassiveEffectsRenderPriority !== NoPriority$1) {1535 var priorityLevel = pendingPassiveEffectsRenderPriority > NormalPriority$1 ? NormalPriority$1 : pendingPassiveEffectsRenderPriority;1536 pendingPassiveEffectsRenderPriority = NoPriority$1;1537 {1538 return runWithPriority$1(priorityLevel, flushPassiveEffectsImpl);1539 }1540 }1541 return false;1542 }1543 function enqueuePendingPassiveHookEffectMount(fiber, effect) {1544 pendingPassiveHookEffectsMount.push(effect, fiber);1545 if (!rootDoesHavePassiveEffects) {1546 rootDoesHavePassiveEffects = true;1547 scheduleCallback(NormalPriority$1, function () {1548 flushPassiveEffects();1549 return null;1550 });1551 }1552 }1553 function enqueuePendingPassiveHookEffectUnmount(fiber, effect) {1554 pendingPassiveHookEffectsUnmount.push(effect, fiber);1555 {1556 fiber.flags |= PassiveUnmountPendingDev;1557 var alternate = fiber.alternate;1558 if (alternate !== null) {1559 alternate.flags |= PassiveUnmountPendingDev;1560 }1561 }1562 if (!rootDoesHavePassiveEffects) {1563 rootDoesHavePassiveEffects = true;1564 scheduleCallback(NormalPriority$1, function () {1565 flushPassiveEffects();1566 return null;1567 });1568 }1569 }1570 function invokePassiveEffectCreate(effect) {1571 var create = effect.create;1572 effect.destroy = create();1573 }1574 function flushPassiveEffectsImpl() {1575 if (rootWithPendingPassiveEffects === null) {1576 return false;1577 }1578 var root = rootWithPendingPassiveEffects;1579 var lanes = pendingPassiveEffectsLanes;1580 rootWithPendingPassiveEffects = null;1581 pendingPassiveEffectsLanes = NoLanes;1582 if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) {1583 {1584 throw Error( "Cannot flush passive effects while already rendering." );1585 }1586 }1587 {1588 markPassiveEffectsStarted(lanes);1589 }1590 {1591 isFlushingPassiveEffects = true;1592 }1593 var prevExecutionContext = executionContext;1594 executionContext |= CommitContext;1595 var prevInteractions = pushInteractions(root); // It's important that ALL pending passive effect destroy functions are called1596 // before ANY passive effect create functions are called.1597 // Otherwise effects in sibling components might interfere with each other.1598 // e.g. a destroy function in one component may unintentionally override a ref1599 // value set by a create function in another component.1600 // Layout effects have the same constraint.1601 // First pass: Destroy stale passive effects.1602 var unmountEffects = pendingPassiveHookEffectsUnmount;1603 pendingPassiveHookEffectsUnmount = [];1604 for (var i = 0; i < unmountEffects.length; i += 2) {1605 var _effect = unmountEffects[i];1606 var fiber = unmountEffects[i + 1];1607 var destroy = _effect.destroy;1608 _effect.destroy = undefined;1609 {1610 fiber.flags &= ~PassiveUnmountPendingDev;1611 var alternate = fiber.alternate;1612 if (alternate !== null) {1613 alternate.flags &= ~PassiveUnmountPendingDev;1614 }1615 }1616 if (typeof destroy === 'function') {1617 {1618 setCurrentFiber(fiber);1619 {1620 invokeGuardedCallback(null, destroy, null);1621 }1622 if (hasCaughtError()) {1623 if (!(fiber !== null)) {1624 {1625 throw Error( "Should be working on an effect." );1626 }1627 }1628 var error = clearCaughtError();1629 captureCommitPhaseError(fiber, error);1630 }1631 resetCurrentFiber();1632 }1633 }1634 } // Second pass: Create new passive effects.1635 var mountEffects = pendingPassiveHookEffectsMount;1636 pendingPassiveHookEffectsMount = [];1637 for (var _i = 0; _i < mountEffects.length; _i += 2) {1638 var _effect2 = mountEffects[_i];1639 var _fiber = mountEffects[_i + 1];1640 {1641 setCurrentFiber(_fiber);1642 {1643 invokeGuardedCallback(null, invokePassiveEffectCreate, null, _effect2);1644 }1645 if (hasCaughtError()) {1646 if (!(_fiber !== null)) {1647 {1648 throw Error( "Should be working on an effect." );1649 }1650 }1651 var _error4 = clearCaughtError();1652 captureCommitPhaseError(_fiber, _error4);1653 }1654 resetCurrentFiber();1655 }1656 } // Note: This currently assumes there are no passive effects on the root fiber1657 // because the root is not part of its own effect list.1658 // This could change in the future.1659 var effect = root.current.firstEffect;1660 while (effect !== null) {1661 var nextNextEffect = effect.nextEffect; // Remove nextEffect pointer to assist GC1662 effect.nextEffect = null;1663 if (effect.flags & Deletion) {1664 detachFiberAfterEffects(effect);1665 }1666 effect = nextNextEffect;1667 }1668 {1669 popInteractions(prevInteractions);1670 finishPendingInteractions(root, lanes);1671 }1672 {1673 isFlushingPassiveEffects = false;1674 }1675 {1676 markPassiveEffectsStopped();1677 }1678 executionContext = prevExecutionContext;1679 flushSyncCallbackQueue(); // If additional passive effects were scheduled, increment a counter. If this1680 // exceeds the limit, we'll fire a warning.1681 nestedPassiveUpdateCount = rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;1682 return true;1683 }1684 function isAlreadyFailedLegacyErrorBoundary(instance) {1685 return legacyErrorBoundariesThatAlreadyFailed !== null && legacyErrorBoundariesThatAlreadyFailed.has(instance);1686 }1687 function markLegacyErrorBoundaryAsFailed(instance) {1688 if (legacyErrorBoundariesThatAlreadyFailed === null) {1689 legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);1690 } else {1691 legacyErrorBoundariesThatAlreadyFailed.add(instance);1692 }1693 }1694 function prepareToThrowUncaughtError(error) {1695 if (!hasUncaughtError) {1696 hasUncaughtError = true;1697 firstUncaughtError = error;1698 }1699 }1700 var onUncaughtError = prepareToThrowUncaughtError;1701 function captureCommitPhaseErrorOnRoot(rootFiber, sourceFiber, error) {1702 var errorInfo = createCapturedValue(error, sourceFiber);1703 var update = createRootErrorUpdate(rootFiber, errorInfo, SyncLane);1704 enqueueUpdate(rootFiber, update);1705 var eventTime = requestEventTime();1706 var root = markUpdateLaneFromFiberToRoot(rootFiber, SyncLane);1707 if (root !== null) {1708 markRootUpdated(root, SyncLane, eventTime);1709 ensureRootIsScheduled(root, eventTime);1710 schedulePendingInteractions(root, SyncLane);1711 }1712 }1713 function captureCommitPhaseError(sourceFiber, error) {1714 if (sourceFiber.tag === HostRoot) {1715 // Error was thrown at the root. There is no parent, so the root1716 // itself should capture it.1717 captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);1718 return;1719 }1720 var fiber = sourceFiber.return;1721 while (fiber !== null) {1722 if (fiber.tag === HostRoot) {1723 captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);1724 return;1725 } else if (fiber.tag === ClassComponent) {1726 var ctor = fiber.type;1727 var instance = fiber.stateNode;1728 if (typeof ctor.getDerivedStateFromError === 'function' || typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance)) {1729 var errorInfo = createCapturedValue(error, sourceFiber);1730 var update = createClassErrorUpdate(fiber, errorInfo, SyncLane);1731 enqueueUpdate(fiber, update);1732 var eventTime = requestEventTime();1733 var root = markUpdateLaneFromFiberToRoot(fiber, SyncLane);1734 if (root !== null) {1735 markRootUpdated(root, SyncLane, eventTime);1736 ensureRootIsScheduled(root, eventTime);1737 schedulePendingInteractions(root, SyncLane);1738 } else {1739 // This component has already been unmounted.1740 // We can't schedule any follow up work for the root because the fiber is already unmounted,1741 // but we can still call the log-only boundary so the error isn't swallowed.1742 //1743 // TODO This is only a temporary bandaid for the old reconciler fork.1744 // We can delete this special case once the new fork is merged.1745 if (typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance)) {1746 try {1747 instance.componentDidCatch(error, errorInfo);1748 } catch (errorToIgnore) {// TODO Ignore this error? Rethrow it?1749 // This is kind of an edge case.1750 }1751 }1752 }1753 return;1754 }1755 }1756 fiber = fiber.return;1757 }1758 }1759 function pingSuspendedRoot(root, wakeable, pingedLanes) {1760 var pingCache = root.pingCache;1761 if (pingCache !== null) {1762 // The wakeable resolved, so we no longer need to memoize, because it will1763 // never be thrown again.1764 pingCache.delete(wakeable);1765 }1766 var eventTime = requestEventTime();1767 markRootPinged(root, pingedLanes);1768 if (workInProgressRoot === root && isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)) {1769 // Received a ping at the same priority level at which we're currently1770 // rendering. We might want to restart this render. This should mirror1771 // the logic of whether or not a root suspends once it completes.1772 // TODO: If we're rendering sync either due to Sync, Batched or expired,1773 // we should probably never restart.1774 // If we're suspended with delay, or if it's a retry, we'll always suspend1775 // so we can always restart.1776 if (workInProgressRootExitStatus === RootSuspendedWithDelay || workInProgressRootExitStatus === RootSuspended && includesOnlyRetries(workInProgressRootRenderLanes) && now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS) {1777 // Restart from the root.1778 prepareFreshStack(root, NoLanes);1779 } else {1780 // Even though we can't restart right now, we might get an1781 // opportunity later. So we mark this render as having a ping.1782 workInProgressRootPingedLanes = mergeLanes(workInProgressRootPingedLanes, pingedLanes);1783 }1784 }1785 ensureRootIsScheduled(root, eventTime);1786 schedulePendingInteractions(root, pingedLanes);1787 }1788 function retryTimedOutBoundary(boundaryFiber, retryLane) {1789 // The boundary fiber (a Suspense component or SuspenseList component)1790 // previously was rendered in its fallback state. One of the promises that1791 // suspended it has resolved, which means at least part of the tree was1792 // likely unblocked. Try rendering again, at a new expiration time.1793 if (retryLane === NoLane) {1794 retryLane = requestRetryLane(boundaryFiber);1795 } // TODO: Special case idle priority?1796 var eventTime = requestEventTime();1797 var root = markUpdateLaneFromFiberToRoot(boundaryFiber, retryLane);1798 if (root !== null) {1799 markRootUpdated(root, retryLane, eventTime);1800 ensureRootIsScheduled(root, eventTime);1801 schedulePendingInteractions(root, retryLane);1802 }1803 }1804 function retryDehydratedSuspenseBoundary(boundaryFiber) {1805 var suspenseState = boundaryFiber.memoizedState;1806 var retryLane = NoLane;1807 if (suspenseState !== null) {1808 retryLane = suspenseState.retryLane;1809 }1810 retryTimedOutBoundary(boundaryFiber, retryLane);1811 }1812 function resolveRetryWakeable(boundaryFiber, wakeable) {1813 var retryLane = NoLane; // Default1814 var retryCache;1815 {1816 switch (boundaryFiber.tag) {1817 case SuspenseComponent:1818 retryCache = boundaryFiber.stateNode;1819 var suspenseState = boundaryFiber.memoizedState;1820 if (suspenseState !== null) {1821 retryLane = suspenseState.retryLane;1822 }1823 break;1824 case SuspenseListComponent:1825 retryCache = boundaryFiber.stateNode;1826 break;1827 default:1828 {1829 {1830 throw Error( "Pinged unknown suspense boundary type. This is probably a bug in React." );1831 }1832 }1833 }1834 }1835 if (retryCache !== null) {1836 // The wakeable resolved, so we no longer need to memoize, because it will1837 // never be thrown again.1838 retryCache.delete(wakeable);1839 }1840 retryTimedOutBoundary(boundaryFiber, retryLane);1841 } // Computes the next Just Noticeable Difference (JND) boundary.1842 // The theory is that a person can't tell the difference between small differences in time.1843 // Therefore, if we wait a bit longer than necessary that won't translate to a noticeable1844 // difference in the experience. However, waiting for longer might mean that we can avoid1845 // showing an intermediate loading state. The longer we have already waited, the harder it1846 // is to tell small differences in time. Therefore, the longer we've already waited,1847 // the longer we can wait additionally. At some point we have to give up though.1848 // We pick a train model where the next boundary commits at a consistent schedule.1849 // These particular numbers are vague estimates. We expect to adjust them based on research.1850 function jnd(timeElapsed) {1851 return timeElapsed < 120 ? 120 : timeElapsed < 480 ? 480 : timeElapsed < 1080 ? 1080 : timeElapsed < 1920 ? 1920 : timeElapsed < 3000 ? 3000 : timeElapsed < 4320 ? 4320 : ceil(timeElapsed / 1960) * 1960;1852 }1853 function checkForNestedUpdates() {1854 if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {1855 nestedUpdateCount = 0;1856 rootWithNestedUpdates = null;1857 {1858 {1859 throw Error( "Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops." );1860 }1861 }1862 }1863 {1864 if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {1865 nestedPassiveUpdateCount = 0;1866 error('Maximum update depth exceeded. This can happen when a component ' + "calls setState inside useEffect, but useEffect either doesn't " + 'have a dependency array, or one of the dependencies changes on ' + 'every render.');1867 }...
ReactFiberWorkLoop.new.js
Source:ReactFiberWorkLoop.new.js
...402 fiber: Fiber,403 lane: Lane,404 eventTime: number,405): FiberRoot | null {406 checkForNestedUpdates();407 warnAboutRenderPhaseUpdatesInDEV(fiber);408 const root = markUpdateLaneFromFiberToRoot(fiber, lane);409 if (root === null) {410 return null;411 }412 if (enableUpdaterTracking) {413 if (isDevToolsPresent) {414 addFiberToLanesMap(root, fiber, lane);415 }416 }417 // Mark that the root has a pending update.418 markRootUpdated(root, lane, eventTime);419 if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {420 if (421 (executionContext & CommitContext) !== NoContext &&422 root === rootCommittingMutationOrLayoutEffects423 ) {424 if (fiber.mode & ProfileMode) {425 let current = fiber;426 while (current !== null) {427 if (current.tag === Profiler) {428 const {id, onNestedUpdateScheduled} = current.memoizedProps;429 if (typeof onNestedUpdateScheduled === 'function') {430 onNestedUpdateScheduled(id);431 }432 }433 current = current.return;434 }435 }436 }437 }438 // TODO: Consolidate with `isInterleavedUpdate` check439 if (root === workInProgressRoot) {440 // Received an update to a tree that's in the middle of rendering. Mark441 // that there was an interleaved update work on this root. Unless the442 // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render443 // phase update. In that case, we don't treat render phase updates as if444 // they were interleaved, for backwards compat reasons.445 if (446 deferRenderPhaseUpdateToNextBatch ||447 (executionContext & RenderContext) === NoContext448 ) {449 workInProgressRootUpdatedLanes = mergeLanes(450 workInProgressRootUpdatedLanes,451 lane,452 );453 }454 if (workInProgressRootExitStatus === RootSuspendedWithDelay) {455 // The root already suspended with a delay, which means this render456 // definitely won't finish. Since we have a new update, let's mark it as457 // suspended now, right before marking the incoming update. This has the458 // effect of interrupting the current render and switching to the update.459 // TODO: Make sure this doesn't override pings that happen while we've460 // already started rendering.461 markRootSuspended(root, workInProgressRootRenderLanes);462 }463 }464 ensureRootIsScheduled(root, eventTime);465 if (466 lane === SyncLane &&467 executionContext === NoContext &&468 (fiber.mode & ConcurrentMode) === NoMode &&469 // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.470 !(__DEV__ && ReactCurrentActQueue.isBatchingLegacy)471 ) {472 // Flush the synchronous work now, unless we're already working or inside473 // a batch. This is intentionally inside scheduleUpdateOnFiber instead of474 // scheduleCallbackForFiber to preserve the ability to schedule a callback475 // without immediately flushing it. We only do this for user-initiated476 // updates, to preserve historical behavior of legacy mode.477 resetRenderTimer();478 flushSyncCallbacksOnlyInLegacyMode();479 }480 return root;481}482// This is split into a separate function so we can mark a fiber with pending483// work without treating it as a typical update that originates from an event;484// e.g. retrying a Suspense boundary isn't an update, but it does schedule work485// on a fiber.486function markUpdateLaneFromFiberToRoot(487 sourceFiber: Fiber,488 lane: Lane,489): FiberRoot | null {490 // Update the source fiber's lanes491 sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);492 let alternate = sourceFiber.alternate;493 if (alternate !== null) {494 alternate.lanes = mergeLanes(alternate.lanes, lane);495 }496 if (__DEV__) {497 if (498 alternate === null &&499 (sourceFiber.flags & (Placement | Hydrating)) !== NoFlags500 ) {501 warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);502 }503 }504 // Walk the parent path to the root and update the child lanes.505 let node = sourceFiber;506 let parent = sourceFiber.return;507 while (parent !== null) {508 parent.childLanes = mergeLanes(parent.childLanes, lane);509 alternate = parent.alternate;510 if (alternate !== null) {511 alternate.childLanes = mergeLanes(alternate.childLanes, lane);512 } else {513 if (__DEV__) {514 if ((parent.flags & (Placement | Hydrating)) !== NoFlags) {515 warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);516 }517 }518 }519 node = parent;520 parent = parent.return;521 }522 if (node.tag === HostRoot) {523 const root: FiberRoot = node.stateNode;524 return root;525 } else {526 return null;527 }528}529export function isInterleavedUpdate(fiber: Fiber, lane: Lane) {530 return (531 // TODO: Optimize slightly by comparing to root that fiber belongs to.532 // Requires some refactoring. Not a big deal though since it's rare for533 // concurrent apps to have more than a single root.534 workInProgressRoot !== null &&535 (fiber.mode & ConcurrentMode) !== NoMode &&536 // If this is a render phase update (i.e. UNSAFE_componentWillReceiveProps),537 // then don't treat this as an interleaved update. This pattern is538 // accompanied by a warning but we haven't fully deprecated it yet. We can539 // remove once the deferRenderPhaseUpdateToNextBatch flag is enabled.540 (deferRenderPhaseUpdateToNextBatch ||541 (executionContext & RenderContext) === NoContext)542 );543}544// Use this function to schedule a task for a root. There's only one task per545// root; if a task was already scheduled, we'll check to make sure the priority546// of the existing task is the same as the priority of the next level that the547// root has work on. This function is called on every update, and right before548// exiting a task.549function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {550 const existingCallbackNode = root.callbackNode;551 // Check if any lanes are being starved by other work. If so, mark them as552 // expired so we know to work on those next.553 markStarvedLanesAsExpired(root, currentTime);554 // Determine the next lanes to work on, and their priority.555 const nextLanes = getNextLanes(556 root,557 root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,558 );559 if (nextLanes === NoLanes) {560 // Special case: There's nothing to work on.561 if (existingCallbackNode !== null) {562 cancelCallback(existingCallbackNode);563 }564 root.callbackNode = null;565 root.callbackPriority = NoLane;566 return;567 }568 // We use the highest priority lane to represent the priority of the callback.569 const newCallbackPriority = getHighestPriorityLane(nextLanes);570 // Check if there's an existing task. We may be able to reuse it.571 const existingCallbackPriority = root.callbackPriority;572 if (573 existingCallbackPriority === newCallbackPriority &&574 // Special case related to `act`. If the currently scheduled task is a575 // Scheduler task, rather than an `act` task, cancel it and re-scheduled576 // on the `act` queue.577 !(578 __DEV__ &&579 ReactCurrentActQueue.current !== null &&580 existingCallbackNode !== fakeActCallbackNode581 )582 ) {583 if (__DEV__) {584 // If we're going to re-use an existing task, it needs to exist.585 // Assume that discrete update microtasks are non-cancellable and null.586 // TODO: Temporary until we confirm this warning is not fired.587 if (588 existingCallbackNode == null &&589 existingCallbackPriority !== SyncLane590 ) {591 console.error(592 'Expected scheduled callback to exist. This error is likely caused by a bug in React. Please file an issue.',593 );594 }595 }596 // The priority hasn't changed. We can reuse the existing task. Exit.597 return;598 }599 if (existingCallbackNode != null) {600 // Cancel the existing callback. We'll schedule a new one below.601 cancelCallback(existingCallbackNode);602 }603 // Schedule a new callback.604 let newCallbackNode;605 if (newCallbackPriority === SyncLane) {606 // Special case: Sync React callbacks are scheduled on a special607 // internal queue608 if (root.tag === LegacyRoot) {609 if (__DEV__ && ReactCurrentActQueue.isBatchingLegacy !== null) {610 ReactCurrentActQueue.didScheduleLegacyUpdate = true;611 }612 scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root));613 } else {614 scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));615 }616 if (supportsMicrotasks) {617 // Flush the queue in a microtask.618 if (__DEV__ && ReactCurrentActQueue.current !== null) {619 // Inside `act`, use our internal `act` queue so that these get flushed620 // at the end of the current scope even when using the sync version621 // of `act`.622 ReactCurrentActQueue.current.push(flushSyncCallbacks);623 } else {624 scheduleMicrotask(flushSyncCallbacks);625 }626 } else {627 // Flush the queue in an Immediate task.628 scheduleCallback(ImmediateSchedulerPriority, flushSyncCallbacks);629 }630 newCallbackNode = null;631 } else {632 let schedulerPriorityLevel;633 switch (lanesToEventPriority(nextLanes)) {634 case DiscreteEventPriority:635 schedulerPriorityLevel = ImmediateSchedulerPriority;636 break;637 case ContinuousEventPriority:638 schedulerPriorityLevel = UserBlockingSchedulerPriority;639 break;640 case DefaultEventPriority:641 schedulerPriorityLevel = NormalSchedulerPriority;642 break;643 case IdleEventPriority:644 schedulerPriorityLevel = IdleSchedulerPriority;645 break;646 default:647 schedulerPriorityLevel = NormalSchedulerPriority;648 break;649 }650 newCallbackNode = scheduleCallback(651 schedulerPriorityLevel,652 performConcurrentWorkOnRoot.bind(null, root),653 );654 }655 root.callbackPriority = newCallbackPriority;656 root.callbackNode = newCallbackNode;657}658// This is the entry point for every concurrent task, i.e. anything that659// goes through Scheduler.660function performConcurrentWorkOnRoot(root, didTimeout) {661 if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {662 resetNestedUpdateFlag();663 }664 // Since we know we're in a React event, we can clear the current665 // event time. The next update will compute a new event time.666 currentEventTime = NoTimestamp;667 currentEventTransitionLane = NoLanes;668 invariant(669 (executionContext & (RenderContext | CommitContext)) === NoContext,670 'Should not already be working.',671 );672 // Flush any pending passive effects before deciding which lanes to work on,673 // in case they schedule additional work.674 const originalCallbackNode = root.callbackNode;675 const didFlushPassiveEffects = flushPassiveEffects();676 if (didFlushPassiveEffects) {677 // Something in the passive effect phase may have canceled the current task.678 // Check if the task node for this root was changed.679 if (root.callbackNode !== originalCallbackNode) {680 // The current task was canceled. Exit. We don't need to call681 // `ensureRootIsScheduled` because the check above implies either that682 // there's a new task, or that there's no remaining work on this root.683 return null;684 } else {685 // Current task was not canceled. Continue.686 }687 }688 // Determine the next lanes to work on, using the fields stored689 // on the root.690 let lanes = getNextLanes(691 root,692 root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,693 );694 if (lanes === NoLanes) {695 // Defensive coding. This is never expected to happen.696 return null;697 }698 // We disable time-slicing in some cases: if the work has been CPU-bound699 // for too long ("expired" work, to prevent starvation), or we're in700 // sync-updates-by-default mode.701 // TODO: We only check `didTimeout` defensively, to account for a Scheduler702 // bug we're still investigating. Once the bug in Scheduler is fixed,703 // we can remove this, since we track expiration ourselves.704 let exitStatus =705 shouldTimeSlice(root, lanes) &&706 (disableSchedulerTimeoutInWorkLoop || !didTimeout)707 ? renderRootConcurrent(root, lanes)708 : renderRootSync(root, lanes);709 if (exitStatus !== RootIncomplete) {710 if (exitStatus === RootErrored) {711 const prevExecutionContext = executionContext;712 executionContext |= RetryAfterError;713 // If an error occurred during hydration,714 // discard server response and fall back to client side render.715 if (root.hydrate) {716 root.hydrate = false;717 if (__DEV__) {718 errorHydratingContainer(root.containerInfo);719 }720 clearContainer(root.containerInfo);721 }722 // If something threw an error, try rendering one more time. We'll render723 // synchronously to block concurrent data mutations, and we'll includes724 // all pending updates are included. If it still fails after the second725 // attempt, we'll give up and commit the resulting tree.726 const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);727 if (errorRetryLanes !== NoLanes) {728 lanes = errorRetryLanes;729 exitStatus = renderRootSync(root, errorRetryLanes);730 }731 executionContext = prevExecutionContext;732 }733 if (exitStatus === RootFatalErrored) {734 const fatalError = workInProgressRootFatalError;735 prepareFreshStack(root, NoLanes);736 markRootSuspended(root, lanes);737 ensureRootIsScheduled(root, now());738 throw fatalError;739 }740 // We now have a consistent tree. The next step is either to commit it,741 // or, if something suspended, wait to commit it after a timeout.742 const finishedWork: Fiber = (root.current.alternate: any);743 root.finishedWork = finishedWork;744 root.finishedLanes = lanes;745 finishConcurrentRender(root, exitStatus, lanes);746 }747 ensureRootIsScheduled(root, now());748 if (root.callbackNode === originalCallbackNode) {749 // The task node scheduled for this root is the same one that's750 // currently executed. Need to return a continuation.751 return performConcurrentWorkOnRoot.bind(null, root);752 }753 return null;754}755function finishConcurrentRender(root, exitStatus, lanes) {756 switch (exitStatus) {757 case RootIncomplete:758 case RootFatalErrored: {759 invariant(false, 'Root did not complete. This is a bug in React.');760 }761 // Flow knows about invariant, so it complains if I add a break762 // statement, but eslint doesn't know about invariant, so it complains763 // if I do. eslint-disable-next-line no-fallthrough764 case RootErrored: {765 // We should have already attempted to retry this tree. If we reached766 // this point, it errored again. Commit it.767 commitRoot(root);768 break;769 }770 case RootSuspended: {771 markRootSuspended(root, lanes);772 // We have an acceptable loading state. We need to figure out if we773 // should immediately commit it or wait a bit.774 if (775 includesOnlyRetries(lanes) &&776 // do not delay if we're inside an act() scope777 !shouldForceFlushFallbacksInDEV()778 ) {779 // This render only included retries, no updates. Throttle committing780 // retries so that we don't show too many loading states too quickly.781 const msUntilTimeout =782 globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now();783 // Don't bother with a very short suspense time.784 if (msUntilTimeout > 10) {785 const nextLanes = getNextLanes(root, NoLanes);786 if (nextLanes !== NoLanes) {787 // There's additional work on this root.788 break;789 }790 const suspendedLanes = root.suspendedLanes;791 if (!isSubsetOfLanes(suspendedLanes, lanes)) {792 // We should prefer to render the fallback of at the last793 // suspended level. Ping the last suspended level to try794 // rendering it again.795 // FIXME: What if the suspended lanes are Idle? Should not restart.796 const eventTime = requestEventTime();797 markRootPinged(root, suspendedLanes, eventTime);798 break;799 }800 // The render is suspended, it hasn't timed out, and there's no801 // lower priority work to do. Instead of committing the fallback802 // immediately, wait for more data to arrive.803 root.timeoutHandle = scheduleTimeout(804 commitRoot.bind(null, root),805 msUntilTimeout,806 );807 break;808 }809 }810 // The work expired. Commit immediately.811 commitRoot(root);812 break;813 }814 case RootSuspendedWithDelay: {815 markRootSuspended(root, lanes);816 if (includesOnlyTransitions(lanes)) {817 // This is a transition, so we should exit without committing a818 // placeholder and without scheduling a timeout. Delay indefinitely819 // until we receive more data.820 break;821 }822 if (!shouldForceFlushFallbacksInDEV()) {823 // This is not a transition, but we did trigger an avoided state.824 // Schedule a placeholder to display after a short delay, using the Just825 // Noticeable Difference.826 // TODO: Is the JND optimization worth the added complexity? If this is827 // the only reason we track the event time, then probably not.828 // Consider removing.829 const mostRecentEventTime = getMostRecentEventTime(root, lanes);830 const eventTimeMs = mostRecentEventTime;831 const timeElapsedMs = now() - eventTimeMs;832 const msUntilTimeout = jnd(timeElapsedMs) - timeElapsedMs;833 // Don't bother with a very short suspense time.834 if (msUntilTimeout > 10) {835 // Instead of committing the fallback immediately, wait for more data836 // to arrive.837 root.timeoutHandle = scheduleTimeout(838 commitRoot.bind(null, root),839 msUntilTimeout,840 );841 break;842 }843 }844 // Commit the placeholder.845 commitRoot(root);846 break;847 }848 case RootCompleted: {849 // The work completed. Ready to commit.850 commitRoot(root);851 break;852 }853 default: {854 invariant(false, 'Unknown root exit status.');855 }856 }857}858function markRootSuspended(root, suspendedLanes) {859 // When suspending, we should always exclude lanes that were pinged or (more860 // rarely, since we try to avoid it) updated during the render phase.861 // TODO: Lol maybe there's a better way to factor this besides this862 // obnoxiously named function :)863 suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);864 suspendedLanes = removeLanes(suspendedLanes, workInProgressRootUpdatedLanes);865 markRootSuspended_dontCallThisOneDirectly(root, suspendedLanes);866}867// This is the entry point for synchronous tasks that don't go868// through Scheduler869function performSyncWorkOnRoot(root) {870 if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {871 syncNestedUpdateFlag();872 }873 invariant(874 (executionContext & (RenderContext | CommitContext)) === NoContext,875 'Should not already be working.',876 );877 flushPassiveEffects();878 let lanes = getNextLanes(root, NoLanes);879 if (!includesSomeLane(lanes, SyncLane)) {880 // There's no remaining sync work left.881 ensureRootIsScheduled(root, now());882 return null;883 }884 let exitStatus = renderRootSync(root, lanes);885 if (root.tag !== LegacyRoot && exitStatus === RootErrored) {886 const prevExecutionContext = executionContext;887 executionContext |= RetryAfterError;888 // If an error occurred during hydration,889 // discard server response and fall back to client side render.890 if (root.hydrate) {891 root.hydrate = false;892 if (__DEV__) {893 errorHydratingContainer(root.containerInfo);894 }895 clearContainer(root.containerInfo);896 }897 // If something threw an error, try rendering one more time. We'll render898 // synchronously to block concurrent data mutations, and we'll includes899 // all pending updates are included. If it still fails after the second900 // attempt, we'll give up and commit the resulting tree.901 const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);902 if (errorRetryLanes !== NoLanes) {903 lanes = errorRetryLanes;904 exitStatus = renderRootSync(root, lanes);905 }906 executionContext = prevExecutionContext;907 }908 if (exitStatus === RootFatalErrored) {909 const fatalError = workInProgressRootFatalError;910 prepareFreshStack(root, NoLanes);911 markRootSuspended(root, lanes);912 ensureRootIsScheduled(root, now());913 throw fatalError;914 }915 // We now have a consistent tree. Because this is a sync render, we916 // will commit it even if something suspended.917 const finishedWork: Fiber = (root.current.alternate: any);918 root.finishedWork = finishedWork;919 root.finishedLanes = lanes;920 commitRoot(root);921 // Before exiting, make sure there's a callback scheduled for the next922 // pending level.923 ensureRootIsScheduled(root, now());924 return null;925}926export function flushRoot(root: FiberRoot, lanes: Lanes) {927 if (lanes !== NoLanes) {928 markRootEntangled(root, mergeLanes(lanes, SyncLane));929 ensureRootIsScheduled(root, now());930 if ((executionContext & (RenderContext | CommitContext)) === NoContext) {931 resetRenderTimer();932 flushSyncCallbacks();933 }934 }935}936export function getExecutionContext(): ExecutionContext {937 return executionContext;938}939export function deferredUpdates<A>(fn: () => A): A {940 const previousPriority = getCurrentUpdatePriority();941 const prevTransition = ReactCurrentBatchConfig.transition;942 try {943 ReactCurrentBatchConfig.transition = 0;944 setCurrentUpdatePriority(DefaultEventPriority);945 return fn();946 } finally {947 setCurrentUpdatePriority(previousPriority);948 ReactCurrentBatchConfig.transition = prevTransition;949 }950}951export function batchedUpdates<A, R>(fn: A => R, a: A): R {952 const prevExecutionContext = executionContext;953 executionContext |= BatchedContext;954 try {955 return fn(a);956 } finally {957 executionContext = prevExecutionContext;958 // If there were legacy sync updates, flush them at the end of the outer959 // most batchedUpdates-like method.960 if (961 executionContext === NoContext &&962 // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.963 !(__DEV__ && ReactCurrentActQueue.isBatchingLegacy)964 ) {965 resetRenderTimer();966 flushSyncCallbacksOnlyInLegacyMode();967 }968 }969}970export function discreteUpdates<A, B, C, D, R>(971 fn: (A, B, C, D) => R,972 a: A,973 b: B,974 c: C,975 d: D,976): R {977 const previousPriority = getCurrentUpdatePriority();978 const prevTransition = ReactCurrentBatchConfig.transition;979 try {980 ReactCurrentBatchConfig.transition = 0;981 setCurrentUpdatePriority(DiscreteEventPriority);982 return fn(a, b, c, d);983 } finally {984 setCurrentUpdatePriority(previousPriority);985 ReactCurrentBatchConfig.transition = prevTransition;986 if (executionContext === NoContext) {987 resetRenderTimer();988 }989 }990}991// Overload the definition to the two valid signatures.992// Warning, this opts-out of checking the function body.993declare function flushSyncWithoutWarningIfAlreadyRendering<R>(fn: () => R): R;994// eslint-disable-next-line no-redeclare995declare function flushSyncWithoutWarningIfAlreadyRendering(): void;996// eslint-disable-next-line no-redeclare997export function flushSyncWithoutWarningIfAlreadyRendering(fn) {998 // In legacy mode, we flush pending passive effects at the beginning of the999 // next event, not at the end of the previous one.1000 if (1001 rootWithPendingPassiveEffects !== null &&1002 rootWithPendingPassiveEffects.tag === LegacyRoot &&1003 (executionContext & (RenderContext | CommitContext)) === NoContext1004 ) {1005 flushPassiveEffects();1006 }1007 const prevExecutionContext = executionContext;1008 executionContext |= BatchedContext;1009 const prevTransition = ReactCurrentBatchConfig.transition;1010 const previousPriority = getCurrentUpdatePriority();1011 try {1012 ReactCurrentBatchConfig.transition = 0;1013 setCurrentUpdatePriority(DiscreteEventPriority);1014 if (fn) {1015 return fn();1016 } else {1017 return undefined;1018 }1019 } finally {1020 setCurrentUpdatePriority(previousPriority);1021 ReactCurrentBatchConfig.transition = prevTransition;1022 executionContext = prevExecutionContext;1023 // Flush the immediate callbacks that were scheduled during this batch.1024 // Note that this will happen even if batchedUpdates is higher up1025 // the stack.1026 if ((executionContext & (RenderContext | CommitContext)) === NoContext) {1027 flushSyncCallbacks();1028 }1029 }1030}1031// Overload the definition to the two valid signatures.1032// Warning, this opts-out of checking the function body.1033declare function flushSync<R>(fn: () => R): R;1034// eslint-disable-next-line no-redeclare1035declare function flushSync(): void;1036// eslint-disable-next-line no-redeclare1037export function flushSync(fn) {1038 if (__DEV__) {1039 if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {1040 console.error(1041 'flushSync was called from inside a lifecycle method. React cannot ' +1042 'flush when React is already rendering. Consider moving this call to ' +1043 'a scheduler task or micro task.',1044 );1045 }1046 }1047 return flushSyncWithoutWarningIfAlreadyRendering(fn);1048}1049export function flushControlled(fn: () => mixed): void {1050 const prevExecutionContext = executionContext;1051 executionContext |= BatchedContext;1052 const prevTransition = ReactCurrentBatchConfig.transition;1053 const previousPriority = getCurrentUpdatePriority();1054 try {1055 ReactCurrentBatchConfig.transition = 0;1056 setCurrentUpdatePriority(DiscreteEventPriority);1057 fn();1058 } finally {1059 setCurrentUpdatePriority(previousPriority);1060 ReactCurrentBatchConfig.transition = prevTransition;1061 executionContext = prevExecutionContext;1062 if (executionContext === NoContext) {1063 // Flush the immediate callbacks that were scheduled during this batch1064 resetRenderTimer();1065 flushSyncCallbacks();1066 }1067 }1068}1069export function pushRenderLanes(fiber: Fiber, lanes: Lanes) {1070 pushToStack(subtreeRenderLanesCursor, subtreeRenderLanes, fiber);1071 subtreeRenderLanes = mergeLanes(subtreeRenderLanes, lanes);1072 workInProgressRootIncludedLanes = mergeLanes(1073 workInProgressRootIncludedLanes,1074 lanes,1075 );1076}1077export function popRenderLanes(fiber: Fiber) {1078 subtreeRenderLanes = subtreeRenderLanesCursor.current;1079 popFromStack(subtreeRenderLanesCursor, fiber);1080}1081function prepareFreshStack(root: FiberRoot, lanes: Lanes) {1082 root.finishedWork = null;1083 root.finishedLanes = NoLanes;1084 const timeoutHandle = root.timeoutHandle;1085 if (timeoutHandle !== noTimeout) {1086 // The root previous suspended and scheduled a timeout to commit a fallback1087 // state. Now that we have additional work, cancel the timeout.1088 root.timeoutHandle = noTimeout;1089 // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above1090 cancelTimeout(timeoutHandle);1091 }1092 if (workInProgress !== null) {1093 let interruptedWork = workInProgress.return;1094 while (interruptedWork !== null) {1095 unwindInterruptedWork(interruptedWork, workInProgressRootRenderLanes);1096 interruptedWork = interruptedWork.return;1097 }1098 }1099 workInProgressRoot = root;1100 workInProgress = createWorkInProgress(root.current, null);1101 workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;1102 workInProgressRootExitStatus = RootIncomplete;1103 workInProgressRootFatalError = null;1104 workInProgressRootSkippedLanes = NoLanes;1105 workInProgressRootUpdatedLanes = NoLanes;1106 workInProgressRootPingedLanes = NoLanes;1107 enqueueInterleavedUpdates();1108 if (__DEV__) {1109 ReactStrictModeWarnings.discardPendingWarnings();1110 }1111}1112function handleError(root, thrownValue): void {1113 do {1114 let erroredWork = workInProgress;1115 try {1116 // Reset module-level state that was set during the render phase.1117 resetContextDependencies();1118 resetHooksAfterThrow();1119 resetCurrentDebugFiberInDEV();1120 // TODO: I found and added this missing line while investigating a1121 // separate issue. Write a regression test using string refs.1122 ReactCurrentOwner.current = null;1123 if (erroredWork === null || erroredWork.return === null) {1124 // Expected to be working on a non-root fiber. This is a fatal error1125 // because there's no ancestor that can handle it; the root is1126 // supposed to capture all errors that weren't caught by an error1127 // boundary.1128 workInProgressRootExitStatus = RootFatalErrored;1129 workInProgressRootFatalError = thrownValue;1130 // Set `workInProgress` to null. This represents advancing to the next1131 // sibling, or the parent if there are no siblings. But since the root1132 // has no siblings nor a parent, we set it to null. Usually this is1133 // handled by `completeUnitOfWork` or `unwindWork`, but since we're1134 // intentionally not calling those, we need set it here.1135 // TODO: Consider calling `unwindWork` to pop the contexts.1136 workInProgress = null;1137 return;1138 }1139 if (enableProfilerTimer && erroredWork.mode & ProfileMode) {1140 // Record the time spent rendering before an error was thrown. This1141 // avoids inaccurate Profiler durations in the case of a1142 // suspended render.1143 stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true);1144 }1145 throwException(1146 root,1147 erroredWork.return,1148 erroredWork,1149 thrownValue,1150 workInProgressRootRenderLanes,1151 );1152 completeUnitOfWork(erroredWork);1153 } catch (yetAnotherThrownValue) {1154 // Something in the return path also threw.1155 thrownValue = yetAnotherThrownValue;1156 if (workInProgress === erroredWork && erroredWork !== null) {1157 // If this boundary has already errored, then we had trouble processing1158 // the error. Bubble it to the next boundary.1159 erroredWork = erroredWork.return;1160 workInProgress = erroredWork;1161 } else {1162 erroredWork = workInProgress;1163 }1164 continue;1165 }1166 // Return to the normal work loop.1167 return;1168 } while (true);1169}1170function pushDispatcher() {1171 const prevDispatcher = ReactCurrentDispatcher.current;1172 ReactCurrentDispatcher.current = ContextOnlyDispatcher;1173 if (prevDispatcher === null) {1174 // The React isomorphic package does not include a default dispatcher.1175 // Instead the first renderer will lazily attach one, in order to give1176 // nicer error messages.1177 return ContextOnlyDispatcher;1178 } else {1179 return prevDispatcher;1180 }1181}1182function popDispatcher(prevDispatcher) {1183 ReactCurrentDispatcher.current = prevDispatcher;1184}1185export function markCommitTimeOfFallback() {1186 globalMostRecentFallbackTime = now();1187}1188export function markSkippedUpdateLanes(lane: Lane | Lanes): void {1189 workInProgressRootSkippedLanes = mergeLanes(1190 lane,1191 workInProgressRootSkippedLanes,1192 );1193}1194export function renderDidSuspend(): void {1195 if (workInProgressRootExitStatus === RootIncomplete) {1196 workInProgressRootExitStatus = RootSuspended;1197 }1198}1199export function renderDidSuspendDelayIfPossible(): void {1200 if (1201 workInProgressRootExitStatus === RootIncomplete ||1202 workInProgressRootExitStatus === RootSuspended1203 ) {1204 workInProgressRootExitStatus = RootSuspendedWithDelay;1205 }1206 // Check if there are updates that we skipped tree that might have unblocked1207 // this render.1208 if (1209 workInProgressRoot !== null &&1210 (includesNonIdleWork(workInProgressRootSkippedLanes) ||1211 includesNonIdleWork(workInProgressRootUpdatedLanes))1212 ) {1213 // Mark the current render as suspended so that we switch to working on1214 // the updates that were skipped. Usually we only suspend at the end of1215 // the render phase.1216 // TODO: We should probably always mark the root as suspended immediately1217 // (inside this function), since by suspending at the end of the render1218 // phase introduces a potential mistake where we suspend lanes that were1219 // pinged or updated while we were rendering.1220 markRootSuspended(workInProgressRoot, workInProgressRootRenderLanes);1221 }1222}1223export function renderDidError() {1224 if (workInProgressRootExitStatus !== RootCompleted) {1225 workInProgressRootExitStatus = RootErrored;1226 }1227}1228// Called during render to determine if anything has suspended.1229// Returns false if we're not sure.1230export function renderHasNotSuspendedYet(): boolean {1231 // If something errored or completed, we can't really be sure,1232 // so those are false.1233 return workInProgressRootExitStatus === RootIncomplete;1234}1235function renderRootSync(root: FiberRoot, lanes: Lanes) {1236 const prevExecutionContext = executionContext;1237 executionContext |= RenderContext;1238 const prevDispatcher = pushDispatcher();1239 // If the root or lanes have changed, throw out the existing stack1240 // and prepare a fresh one. Otherwise we'll continue where we left off.1241 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1242 if (enableUpdaterTracking) {1243 if (isDevToolsPresent) {1244 const memoizedUpdaters = root.memoizedUpdaters;1245 if (memoizedUpdaters.size > 0) {1246 restorePendingUpdaters(root, workInProgressRootRenderLanes);1247 memoizedUpdaters.clear();1248 }1249 // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.1250 // If we bailout on this work, we'll move them back (like above).1251 // It's important to move them now in case the work spawns more work at the same priority with different updaters.1252 // That way we can keep the current update and future updates separate.1253 movePendingFibersToMemoized(root, lanes);1254 }1255 }1256 prepareFreshStack(root, lanes);1257 }1258 if (__DEV__) {1259 if (enableDebugTracing) {1260 logRenderStarted(lanes);1261 }1262 }1263 if (enableSchedulingProfiler) {1264 markRenderStarted(lanes);1265 }1266 do {1267 try {1268 workLoopSync();1269 break;1270 } catch (thrownValue) {1271 handleError(root, thrownValue);1272 }1273 } while (true);1274 resetContextDependencies();1275 executionContext = prevExecutionContext;1276 popDispatcher(prevDispatcher);1277 if (workInProgress !== null) {1278 // This is a sync render, so we should have finished the whole tree.1279 invariant(1280 false,1281 'Cannot commit an incomplete root. This error is likely caused by a ' +1282 'bug in React. Please file an issue.',1283 );1284 }1285 if (__DEV__) {1286 if (enableDebugTracing) {1287 logRenderStopped();1288 }1289 }1290 if (enableSchedulingProfiler) {1291 markRenderStopped();1292 }1293 // Set this to null to indicate there's no in-progress render.1294 workInProgressRoot = null;1295 workInProgressRootRenderLanes = NoLanes;1296 return workInProgressRootExitStatus;1297}1298// The work loop is an extremely hot path. Tell Closure not to inline it.1299/** @noinline */1300function workLoopSync() {1301 // Already timed out, so perform work without checking if we need to yield.1302 while (workInProgress !== null) {1303 performUnitOfWork(workInProgress);1304 }1305}1306function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {1307 const prevExecutionContext = executionContext;1308 executionContext |= RenderContext;1309 const prevDispatcher = pushDispatcher();1310 // If the root or lanes have changed, throw out the existing stack1311 // and prepare a fresh one. Otherwise we'll continue where we left off.1312 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1313 if (enableUpdaterTracking) {1314 if (isDevToolsPresent) {1315 const memoizedUpdaters = root.memoizedUpdaters;1316 if (memoizedUpdaters.size > 0) {1317 restorePendingUpdaters(root, workInProgressRootRenderLanes);1318 memoizedUpdaters.clear();1319 }1320 // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.1321 // If we bailout on this work, we'll move them back (like above).1322 // It's important to move them now in case the work spawns more work at the same priority with different updaters.1323 // That way we can keep the current update and future updates separate.1324 movePendingFibersToMemoized(root, lanes);1325 }1326 }1327 resetRenderTimer();1328 prepareFreshStack(root, lanes);1329 }1330 if (__DEV__) {1331 if (enableDebugTracing) {1332 logRenderStarted(lanes);1333 }1334 }1335 if (enableSchedulingProfiler) {1336 markRenderStarted(lanes);1337 }1338 do {1339 try {1340 workLoopConcurrent();1341 break;1342 } catch (thrownValue) {1343 handleError(root, thrownValue);1344 }1345 } while (true);1346 resetContextDependencies();1347 popDispatcher(prevDispatcher);1348 executionContext = prevExecutionContext;1349 if (__DEV__) {1350 if (enableDebugTracing) {1351 logRenderStopped();1352 }1353 }1354 // Check if the tree has completed.1355 if (workInProgress !== null) {1356 // Still work remaining.1357 if (enableSchedulingProfiler) {1358 markRenderYielded();1359 }1360 return RootIncomplete;1361 } else {1362 // Completed the tree.1363 if (enableSchedulingProfiler) {1364 markRenderStopped();1365 }1366 // Set this to null to indicate there's no in-progress render.1367 workInProgressRoot = null;1368 workInProgressRootRenderLanes = NoLanes;1369 // Return the final exit status.1370 return workInProgressRootExitStatus;1371 }1372}1373/** @noinline */1374function workLoopConcurrent() {1375 // Perform work until Scheduler asks us to yield1376 while (workInProgress !== null && !shouldYield()) {1377 performUnitOfWork(workInProgress);1378 }1379}1380function performUnitOfWork(unitOfWork: Fiber): void {1381 // The current, flushed, state of this fiber is the alternate. Ideally1382 // nothing should rely on this, but relying on it here means that we don't1383 // need an additional field on the work in progress.1384 const current = unitOfWork.alternate;1385 setCurrentDebugFiberInDEV(unitOfWork);1386 let next;1387 if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {1388 startProfilerTimer(unitOfWork);1389 next = beginWork(current, unitOfWork, subtreeRenderLanes);1390 stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);1391 } else {1392 next = beginWork(current, unitOfWork, subtreeRenderLanes);1393 }1394 resetCurrentDebugFiberInDEV();1395 unitOfWork.memoizedProps = unitOfWork.pendingProps;1396 if (next === null) {1397 // If this doesn't spawn new work, complete the current work.1398 completeUnitOfWork(unitOfWork);1399 } else {1400 workInProgress = next;1401 }1402 ReactCurrentOwner.current = null;1403}1404function completeUnitOfWork(unitOfWork: Fiber): void {1405 // Attempt to complete the current unit of work, then move to the next1406 // sibling. If there are no more siblings, return to the parent fiber.1407 let completedWork = unitOfWork;1408 do {1409 // The current, flushed, state of this fiber is the alternate. Ideally1410 // nothing should rely on this, but relying on it here means that we don't1411 // need an additional field on the work in progress.1412 const current = completedWork.alternate;1413 const returnFiber = completedWork.return;1414 // Check if the work completed or if something threw.1415 if ((completedWork.flags & Incomplete) === NoFlags) {1416 setCurrentDebugFiberInDEV(completedWork);1417 let next;1418 if (1419 !enableProfilerTimer ||1420 (completedWork.mode & ProfileMode) === NoMode1421 ) {1422 next = completeWork(current, completedWork, subtreeRenderLanes);1423 } else {1424 startProfilerTimer(completedWork);1425 next = completeWork(current, completedWork, subtreeRenderLanes);1426 // Update render duration assuming we didn't error.1427 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1428 }1429 resetCurrentDebugFiberInDEV();1430 if (next !== null) {1431 // Completing this fiber spawned new work. Work on that next.1432 workInProgress = next;1433 return;1434 }1435 } else {1436 // This fiber did not complete because something threw. Pop values off1437 // the stack without entering the complete phase. If this is a boundary,1438 // capture values if possible.1439 const next = unwindWork(completedWork, subtreeRenderLanes);1440 // Because this fiber did not complete, don't reset its lanes.1441 if (next !== null) {1442 // If completing this work spawned new work, do that next. We'll come1443 // back here again.1444 // Since we're restarting, remove anything that is not a host effect1445 // from the effect tag.1446 next.flags &= HostEffectMask;1447 workInProgress = next;1448 return;1449 }1450 if (1451 enableProfilerTimer &&1452 (completedWork.mode & ProfileMode) !== NoMode1453 ) {1454 // Record the render duration for the fiber that errored.1455 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1456 // Include the time spent working on failed children before continuing.1457 let actualDuration = completedWork.actualDuration;1458 let child = completedWork.child;1459 while (child !== null) {1460 actualDuration += child.actualDuration;1461 child = child.sibling;1462 }1463 completedWork.actualDuration = actualDuration;1464 }1465 if (returnFiber !== null) {1466 // Mark the parent fiber as incomplete and clear its subtree flags.1467 returnFiber.flags |= Incomplete;1468 returnFiber.subtreeFlags = NoFlags;1469 returnFiber.deletions = null;1470 }1471 }1472 const siblingFiber = completedWork.sibling;1473 if (siblingFiber !== null) {1474 // If there is more work to do in this returnFiber, do that next.1475 workInProgress = siblingFiber;1476 return;1477 }1478 // Otherwise, return to the parent1479 completedWork = returnFiber;1480 // Update the next thing we're working on in case something throws.1481 workInProgress = completedWork;1482 } while (completedWork !== null);1483 // We've reached the root.1484 if (workInProgressRootExitStatus === RootIncomplete) {1485 workInProgressRootExitStatus = RootCompleted;1486 }1487}1488function commitRoot(root) {1489 // TODO: This no longer makes any sense. We already wrap the mutation and1490 // layout phases. Should be able to remove.1491 const previousUpdateLanePriority = getCurrentUpdatePriority();1492 const prevTransition = ReactCurrentBatchConfig.transition;1493 try {1494 ReactCurrentBatchConfig.transition = 0;1495 setCurrentUpdatePriority(DiscreteEventPriority);1496 commitRootImpl(root, previousUpdateLanePriority);1497 } finally {1498 ReactCurrentBatchConfig.transition = prevTransition;1499 setCurrentUpdatePriority(previousUpdateLanePriority);1500 }1501 return null;1502}1503function commitRootImpl(root, renderPriorityLevel) {1504 do {1505 // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which1506 // means `flushPassiveEffects` will sometimes result in additional1507 // passive effects. So we need to keep flushing in a loop until there are1508 // no more pending effects.1509 // TODO: Might be better if `flushPassiveEffects` did not automatically1510 // flush synchronous work at the end, to avoid factoring hazards like this.1511 flushPassiveEffects();1512 } while (rootWithPendingPassiveEffects !== null);1513 flushRenderPhaseStrictModeWarningsInDEV();1514 invariant(1515 (executionContext & (RenderContext | CommitContext)) === NoContext,1516 'Should not already be working.',1517 );1518 const finishedWork = root.finishedWork;1519 const lanes = root.finishedLanes;1520 if (__DEV__) {1521 if (enableDebugTracing) {1522 logCommitStarted(lanes);1523 }1524 }1525 if (enableSchedulingProfiler) {1526 markCommitStarted(lanes);1527 }1528 if (finishedWork === null) {1529 if (__DEV__) {1530 if (enableDebugTracing) {1531 logCommitStopped();1532 }1533 }1534 if (enableSchedulingProfiler) {1535 markCommitStopped();1536 }1537 return null;1538 } else {1539 if (__DEV__) {1540 if (lanes === NoLanes) {1541 console.error(1542 'root.finishedLanes should not be empty during a commit. This is a ' +1543 'bug in React.',1544 );1545 }1546 }1547 }1548 root.finishedWork = null;1549 root.finishedLanes = NoLanes;1550 invariant(1551 finishedWork !== root.current,1552 'Cannot commit the same tree as before. This error is likely caused by ' +1553 'a bug in React. Please file an issue.',1554 );1555 // commitRoot never returns a continuation; it always finishes synchronously.1556 // So we can clear these now to allow a new callback to be scheduled.1557 root.callbackNode = null;1558 root.callbackPriority = NoLane;1559 // Update the first and last pending times on this root. The new first1560 // pending time is whatever is left on the root fiber.1561 let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);1562 markRootFinished(root, remainingLanes);1563 if (root === workInProgressRoot) {1564 // We can reset these now that they are finished.1565 workInProgressRoot = null;1566 workInProgress = null;1567 workInProgressRootRenderLanes = NoLanes;1568 } else {1569 // This indicates that the last root we worked on is not the same one that1570 // we're committing now. This most commonly happens when a suspended root1571 // times out.1572 }1573 // If there are pending passive effects, schedule a callback to process them.1574 // Do this as early as possible, so it is queued before anything else that1575 // might get scheduled in the commit phase. (See #16714.)1576 // TODO: Delete all other places that schedule the passive effect callback1577 // They're redundant.1578 if (1579 (finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||1580 (finishedWork.flags & PassiveMask) !== NoFlags1581 ) {1582 if (!rootDoesHavePassiveEffects) {1583 rootDoesHavePassiveEffects = true;1584 scheduleCallback(NormalSchedulerPriority, () => {1585 flushPassiveEffects();1586 return null;1587 });1588 }1589 }1590 // Check if there are any effects in the whole tree.1591 // TODO: This is left over from the effect list implementation, where we had1592 // to check for the existence of `firstEffect` to satisfy Flow. I think the1593 // only other reason this optimization exists is because it affects profiling.1594 // Reconsider whether this is necessary.1595 const subtreeHasEffects =1596 (finishedWork.subtreeFlags &1597 (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1598 NoFlags;1599 const rootHasEffect =1600 (finishedWork.flags &1601 (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1602 NoFlags;1603 if (subtreeHasEffects || rootHasEffect) {1604 const prevTransition = ReactCurrentBatchConfig.transition;1605 ReactCurrentBatchConfig.transition = 0;1606 const previousPriority = getCurrentUpdatePriority();1607 setCurrentUpdatePriority(DiscreteEventPriority);1608 const prevExecutionContext = executionContext;1609 executionContext |= CommitContext;1610 // Reset this to null before calling lifecycles1611 ReactCurrentOwner.current = null;1612 // The commit phase is broken into several sub-phases. We do a separate pass1613 // of the effect list for each phase: all mutation effects come before all1614 // layout effects, and so on.1615 // The first phase a "before mutation" phase. We use this phase to read the1616 // state of the host tree right before we mutate it. This is where1617 // getSnapshotBeforeUpdate is called.1618 const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(1619 root,1620 finishedWork,1621 );1622 if (enableProfilerTimer) {1623 // Mark the current commit time to be shared by all Profilers in this1624 // batch. This enables them to be grouped later.1625 recordCommitTime();1626 }1627 if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {1628 // Track the root here, rather than in commitLayoutEffects(), because of ref setters.1629 // Updates scheduled during ref detachment should also be flagged.1630 rootCommittingMutationOrLayoutEffects = root;1631 }1632 // The next phase is the mutation phase, where we mutate the host tree.1633 commitMutationEffects(root, finishedWork, lanes);1634 if (enableCreateEventHandleAPI) {1635 if (shouldFireAfterActiveInstanceBlur) {1636 afterActiveInstanceBlur();1637 }1638 }1639 resetAfterCommit(root.containerInfo);1640 // The work-in-progress tree is now the current tree. This must come after1641 // the mutation phase, so that the previous tree is still current during1642 // componentWillUnmount, but before the layout phase, so that the finished1643 // work is current during componentDidMount/Update.1644 root.current = finishedWork;1645 // The next phase is the layout phase, where we call effects that read1646 // the host tree after it's been mutated. The idiomatic use case for this is1647 // layout, but class component lifecycles also fire here for legacy reasons.1648 if (__DEV__) {1649 if (enableDebugTracing) {1650 logLayoutEffectsStarted(lanes);1651 }1652 }1653 if (enableSchedulingProfiler) {1654 markLayoutEffectsStarted(lanes);1655 }1656 commitLayoutEffects(finishedWork, root, lanes);1657 if (__DEV__) {1658 if (enableDebugTracing) {1659 logLayoutEffectsStopped();1660 }1661 }1662 if (enableSchedulingProfiler) {1663 markLayoutEffectsStopped();1664 }1665 if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {1666 rootCommittingMutationOrLayoutEffects = null;1667 }1668 // Tell Scheduler to yield at the end of the frame, so the browser has an1669 // opportunity to paint.1670 requestPaint();1671 executionContext = prevExecutionContext;1672 // Reset the priority to the previous non-sync value.1673 setCurrentUpdatePriority(previousPriority);1674 ReactCurrentBatchConfig.transition = prevTransition;1675 } else {1676 // No effects.1677 root.current = finishedWork;1678 // Measure these anyway so the flamegraph explicitly shows that there were1679 // no effects.1680 // TODO: Maybe there's a better way to report this.1681 if (enableProfilerTimer) {1682 recordCommitTime();1683 }1684 }1685 const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;1686 if (rootDoesHavePassiveEffects) {1687 // This commit has passive effects. Stash a reference to them. But don't1688 // schedule a callback until after flushing layout work.1689 rootDoesHavePassiveEffects = false;1690 rootWithPendingPassiveEffects = root;1691 pendingPassiveEffectsLanes = lanes;1692 }1693 // Read this again, since an effect might have updated it1694 remainingLanes = root.pendingLanes;1695 // Check if there's remaining work on this root1696 if (remainingLanes === NoLanes) {1697 // If there's no remaining work, we can clear the set of already failed1698 // error boundaries.1699 legacyErrorBoundariesThatAlreadyFailed = null;1700 }1701 if (__DEV__ && enableStrictEffects) {1702 if (!rootDidHavePassiveEffects) {1703 commitDoubleInvokeEffectsInDEV(root.current, false);1704 }1705 }1706 if (includesSomeLane(remainingLanes, (SyncLane: Lane))) {1707 if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {1708 markNestedUpdateScheduled();1709 }1710 // Count the number of times the root synchronously re-renders without1711 // finishing. If there are too many, it indicates an infinite update loop.1712 if (root === rootWithNestedUpdates) {1713 nestedUpdateCount++;1714 } else {1715 nestedUpdateCount = 0;1716 rootWithNestedUpdates = root;1717 }1718 } else {1719 nestedUpdateCount = 0;1720 }1721 onCommitRootDevTools(finishedWork.stateNode, renderPriorityLevel);1722 if (enableUpdaterTracking) {1723 if (isDevToolsPresent) {1724 root.memoizedUpdaters.clear();1725 }1726 }1727 if (__DEV__) {1728 onCommitRootTestSelector();1729 }1730 // Always call this before exiting `commitRoot`, to ensure that any1731 // additional work on this root is scheduled.1732 ensureRootIsScheduled(root, now());1733 if (hasUncaughtError) {1734 hasUncaughtError = false;1735 const error = firstUncaughtError;1736 firstUncaughtError = null;1737 throw error;1738 }1739 // If the passive effects are the result of a discrete render, flush them1740 // synchronously at the end of the current task so that the result is1741 // immediately observable. Otherwise, we assume that they are not1742 // order-dependent and do not need to be observed by external systems, so we1743 // can wait until after paint.1744 // TODO: We can optimize this by not scheduling the callback earlier. Since we1745 // currently schedule the callback in multiple places, will wait until those1746 // are consolidated.1747 if (1748 includesSomeLane(pendingPassiveEffectsLanes, SyncLane) &&1749 root.tag !== LegacyRoot1750 ) {1751 flushPassiveEffects();1752 }1753 // If layout work was scheduled, flush it now.1754 flushSyncCallbacks();1755 if (__DEV__) {1756 if (enableDebugTracing) {1757 logCommitStopped();1758 }1759 }1760 if (enableSchedulingProfiler) {1761 markCommitStopped();1762 }1763 return null;1764}1765export function flushPassiveEffects(): boolean {1766 // Returns whether passive effects were flushed.1767 // TODO: Combine this check with the one in flushPassiveEFfectsImpl. We should1768 // probably just combine the two functions. I believe they were only separate1769 // in the first place because we used to wrap it with1770 // `Scheduler.runWithPriority`, which accepts a function. But now we track the1771 // priority within React itself, so we can mutate the variable directly.1772 if (rootWithPendingPassiveEffects !== null) {1773 const renderPriority = lanesToEventPriority(pendingPassiveEffectsLanes);1774 const priority = lowerEventPriority(DefaultEventPriority, renderPriority);1775 const prevTransition = ReactCurrentBatchConfig.transition;1776 const previousPriority = getCurrentUpdatePriority();1777 try {1778 ReactCurrentBatchConfig.transition = 0;1779 setCurrentUpdatePriority(priority);1780 return flushPassiveEffectsImpl();1781 } finally {1782 setCurrentUpdatePriority(previousPriority);1783 ReactCurrentBatchConfig.transition = prevTransition;1784 }1785 }1786 return false;1787}1788export function enqueuePendingPassiveProfilerEffect(fiber: Fiber): void {1789 if (enableProfilerTimer && enableProfilerCommitHooks) {1790 pendingPassiveProfilerEffects.push(fiber);1791 if (!rootDoesHavePassiveEffects) {1792 rootDoesHavePassiveEffects = true;1793 scheduleCallback(NormalSchedulerPriority, () => {1794 flushPassiveEffects();1795 return null;1796 });1797 }1798 }1799}1800function flushPassiveEffectsImpl() {1801 if (rootWithPendingPassiveEffects === null) {1802 return false;1803 }1804 const root = rootWithPendingPassiveEffects;1805 const lanes = pendingPassiveEffectsLanes;1806 rootWithPendingPassiveEffects = null;1807 // TODO: This is sometimes out of sync with rootWithPendingPassiveEffects.1808 // Figure out why and fix it. It's not causing any known issues (probably1809 // because it's only used for profiling), but it's a refactor hazard.1810 pendingPassiveEffectsLanes = NoLanes;1811 invariant(1812 (executionContext & (RenderContext | CommitContext)) === NoContext,1813 'Cannot flush passive effects while already rendering.',1814 );1815 if (__DEV__) {1816 if (enableDebugTracing) {1817 logPassiveEffectsStarted(lanes);1818 }1819 }1820 if (enableSchedulingProfiler) {1821 markPassiveEffectsStarted(lanes);1822 }1823 const prevExecutionContext = executionContext;1824 executionContext |= CommitContext;1825 commitPassiveUnmountEffects(root.current);1826 commitPassiveMountEffects(root, root.current);1827 // TODO: Move to commitPassiveMountEffects1828 if (enableProfilerTimer && enableProfilerCommitHooks) {1829 const profilerEffects = pendingPassiveProfilerEffects;1830 pendingPassiveProfilerEffects = [];1831 for (let i = 0; i < profilerEffects.length; i++) {1832 const fiber = ((profilerEffects[i]: any): Fiber);1833 commitPassiveEffectDurations(root, fiber);1834 }1835 }1836 if (__DEV__) {1837 if (enableDebugTracing) {1838 logPassiveEffectsStopped();1839 }1840 }1841 if (enableSchedulingProfiler) {1842 markPassiveEffectsStopped();1843 }1844 if (__DEV__ && enableStrictEffects) {1845 commitDoubleInvokeEffectsInDEV(root.current, true);1846 }1847 executionContext = prevExecutionContext;1848 flushSyncCallbacks();1849 // If additional passive effects were scheduled, increment a counter. If this1850 // exceeds the limit, we'll fire a warning.1851 nestedPassiveUpdateCount =1852 rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;1853 // TODO: Move to commitPassiveMountEffects1854 onPostCommitRootDevTools(root);1855 if (enableProfilerTimer && enableProfilerCommitHooks) {1856 const stateNode = root.current.stateNode;1857 stateNode.effectDuration = 0;1858 stateNode.passiveEffectDuration = 0;1859 }1860 return true;1861}1862export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {1863 return (1864 legacyErrorBoundariesThatAlreadyFailed !== null &&1865 legacyErrorBoundariesThatAlreadyFailed.has(instance)1866 );1867}1868export function markLegacyErrorBoundaryAsFailed(instance: mixed) {1869 if (legacyErrorBoundariesThatAlreadyFailed === null) {1870 legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);1871 } else {1872 legacyErrorBoundariesThatAlreadyFailed.add(instance);1873 }1874}1875function prepareToThrowUncaughtError(error: mixed) {1876 if (!hasUncaughtError) {1877 hasUncaughtError = true;1878 firstUncaughtError = error;1879 }1880}1881export const onUncaughtError = prepareToThrowUncaughtError;1882function captureCommitPhaseErrorOnRoot(1883 rootFiber: Fiber,1884 sourceFiber: Fiber,1885 error: mixed,1886) {1887 const errorInfo = createCapturedValue(error, sourceFiber);1888 const update = createRootErrorUpdate(rootFiber, errorInfo, (SyncLane: Lane));1889 enqueueUpdate(rootFiber, update, (SyncLane: Lane));1890 const eventTime = requestEventTime();1891 const root = markUpdateLaneFromFiberToRoot(rootFiber, (SyncLane: Lane));1892 if (root !== null) {1893 markRootUpdated(root, SyncLane, eventTime);1894 ensureRootIsScheduled(root, eventTime);1895 }1896}1897export function captureCommitPhaseError(1898 sourceFiber: Fiber,1899 nearestMountedAncestor: Fiber | null,1900 error: mixed,1901) {1902 if (sourceFiber.tag === HostRoot) {1903 // Error was thrown at the root. There is no parent, so the root1904 // itself should capture it.1905 captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);1906 return;1907 }1908 let fiber = null;1909 if (skipUnmountedBoundaries) {1910 fiber = nearestMountedAncestor;1911 } else {1912 fiber = sourceFiber.return;1913 }1914 while (fiber !== null) {1915 if (fiber.tag === HostRoot) {1916 captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);1917 return;1918 } else if (fiber.tag === ClassComponent) {1919 const ctor = fiber.type;1920 const instance = fiber.stateNode;1921 if (1922 typeof ctor.getDerivedStateFromError === 'function' ||1923 (typeof instance.componentDidCatch === 'function' &&1924 !isAlreadyFailedLegacyErrorBoundary(instance))1925 ) {1926 const errorInfo = createCapturedValue(error, sourceFiber);1927 const update = createClassErrorUpdate(1928 fiber,1929 errorInfo,1930 (SyncLane: Lane),1931 );1932 enqueueUpdate(fiber, update, (SyncLane: Lane));1933 const eventTime = requestEventTime();1934 const root = markUpdateLaneFromFiberToRoot(fiber, (SyncLane: Lane));1935 if (root !== null) {1936 markRootUpdated(root, SyncLane, eventTime);1937 ensureRootIsScheduled(root, eventTime);1938 }1939 return;1940 }1941 }1942 fiber = fiber.return;1943 }1944 if (__DEV__) {1945 // TODO: Until we re-land skipUnmountedBoundaries (see #20147), this warning1946 // will fire for errors that are thrown by destroy functions inside deleted1947 // trees. What it should instead do is propagate the error to the parent of1948 // the deleted tree. In the meantime, do not add this warning to the1949 // allowlist; this is only for our internal use.1950 console.error(1951 'Internal React error: Attempted to capture a commit phase error ' +1952 'inside a detached tree. This indicates a bug in React. Likely ' +1953 'causes include deleting the same fiber more than once, committing an ' +1954 'already-finished tree, or an inconsistent return pointer.\n\n' +1955 'Error message:\n\n%s',1956 error,1957 );1958 }1959}1960export function pingSuspendedRoot(1961 root: FiberRoot,1962 wakeable: Wakeable,1963 pingedLanes: Lanes,1964) {1965 const pingCache = root.pingCache;1966 if (pingCache !== null) {1967 // The wakeable resolved, so we no longer need to memoize, because it will1968 // never be thrown again.1969 pingCache.delete(wakeable);1970 }1971 const eventTime = requestEventTime();1972 markRootPinged(root, pingedLanes, eventTime);1973 if (1974 workInProgressRoot === root &&1975 isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)1976 ) {1977 // Received a ping at the same priority level at which we're currently1978 // rendering. We might want to restart this render. This should mirror1979 // the logic of whether or not a root suspends once it completes.1980 // TODO: If we're rendering sync either due to Sync, Batched or expired,1981 // we should probably never restart.1982 // If we're suspended with delay, or if it's a retry, we'll always suspend1983 // so we can always restart.1984 if (1985 workInProgressRootExitStatus === RootSuspendedWithDelay ||1986 (workInProgressRootExitStatus === RootSuspended &&1987 includesOnlyRetries(workInProgressRootRenderLanes) &&1988 now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS)1989 ) {1990 // Restart from the root.1991 prepareFreshStack(root, NoLanes);1992 } else {1993 // Even though we can't restart right now, we might get an1994 // opportunity later. So we mark this render as having a ping.1995 workInProgressRootPingedLanes = mergeLanes(1996 workInProgressRootPingedLanes,1997 pingedLanes,1998 );1999 }2000 }2001 ensureRootIsScheduled(root, eventTime);2002}2003function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {2004 // The boundary fiber (a Suspense component or SuspenseList component)2005 // previously was rendered in its fallback state. One of the promises that2006 // suspended it has resolved, which means at least part of the tree was2007 // likely unblocked. Try rendering again, at a new lanes.2008 if (retryLane === NoLane) {2009 // TODO: Assign this to `suspenseState.retryLane`? to avoid2010 // unnecessary entanglement?2011 retryLane = requestRetryLane(boundaryFiber);2012 }2013 // TODO: Special case idle priority?2014 const eventTime = requestEventTime();2015 const root = markUpdateLaneFromFiberToRoot(boundaryFiber, retryLane);2016 if (root !== null) {2017 markRootUpdated(root, retryLane, eventTime);2018 ensureRootIsScheduled(root, eventTime);2019 }2020}2021export function retryDehydratedSuspenseBoundary(boundaryFiber: Fiber) {2022 const suspenseState: null | SuspenseState = boundaryFiber.memoizedState;2023 let retryLane = NoLane;2024 if (suspenseState !== null) {2025 retryLane = suspenseState.retryLane;2026 }2027 retryTimedOutBoundary(boundaryFiber, retryLane);2028}2029export function resolveRetryWakeable(boundaryFiber: Fiber, wakeable: Wakeable) {2030 let retryLane = NoLane; // Default2031 let retryCache: WeakSet<Wakeable> | Set<Wakeable> | null;2032 if (enableSuspenseServerRenderer) {2033 switch (boundaryFiber.tag) {2034 case SuspenseComponent:2035 retryCache = boundaryFiber.stateNode;2036 const suspenseState: null | SuspenseState = boundaryFiber.memoizedState;2037 if (suspenseState !== null) {2038 retryLane = suspenseState.retryLane;2039 }2040 break;2041 case SuspenseListComponent:2042 retryCache = boundaryFiber.stateNode;2043 break;2044 default:2045 invariant(2046 false,2047 'Pinged unknown suspense boundary type. ' +2048 'This is probably a bug in React.',2049 );2050 }2051 } else {2052 retryCache = boundaryFiber.stateNode;2053 }2054 if (retryCache !== null) {2055 // The wakeable resolved, so we no longer need to memoize, because it will2056 // never be thrown again.2057 retryCache.delete(wakeable);2058 }2059 retryTimedOutBoundary(boundaryFiber, retryLane);2060}2061// Computes the next Just Noticeable Difference (JND) boundary.2062// The theory is that a person can't tell the difference between small differences in time.2063// Therefore, if we wait a bit longer than necessary that won't translate to a noticeable2064// difference in the experience. However, waiting for longer might mean that we can avoid2065// showing an intermediate loading state. The longer we have already waited, the harder it2066// is to tell small differences in time. Therefore, the longer we've already waited,2067// the longer we can wait additionally. At some point we have to give up though.2068// We pick a train model where the next boundary commits at a consistent schedule.2069// These particular numbers are vague estimates. We expect to adjust them based on research.2070function jnd(timeElapsed: number) {2071 return timeElapsed < 1202072 ? 1202073 : timeElapsed < 4802074 ? 4802075 : timeElapsed < 10802076 ? 10802077 : timeElapsed < 19202078 ? 19202079 : timeElapsed < 30002080 ? 30002081 : timeElapsed < 43202082 ? 43202083 : ceil(timeElapsed / 1960) * 1960;2084}2085function checkForNestedUpdates() {2086 if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {2087 nestedUpdateCount = 0;2088 rootWithNestedUpdates = null;2089 invariant(2090 false,2091 'Maximum update depth exceeded. This can happen when a component ' +2092 'repeatedly calls setState inside componentWillUpdate or ' +2093 'componentDidUpdate. React limits the number of nested updates to ' +2094 'prevent infinite loops.',2095 );2096 }2097 if (__DEV__) {2098 if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {2099 nestedPassiveUpdateCount = 0;...
ReactFiberScheduler.js
Source:ReactFiberScheduler.js
...284export function scheduleUpdateOnFiber(285 fiber: Fiber,286 expirationTime: ExpirationTime,287) {288 checkForNestedUpdates();289 warnAboutInvalidUpdatesOnClassComponentsInDEV(fiber);290 const root = markUpdateTimeFromFiberToRoot(fiber, expirationTime);291 if (root === null) {292 warnAboutUpdateOnUnmountedFiberInDEV(fiber);293 return;294 }295 root.pingTime = NoWork;296 checkForInterruption(fiber, expirationTime);297 recordScheduleUpdate();298 if (expirationTime === Sync) {299 if (workPhase === LegacyUnbatchedPhase) {300 // This is a legacy edge case. The initial mount of a ReactDOM.render-ed301 // root inside of batchedUpdates should be synchronous, but layout updates302 // should be deferred until the end of the batch.303 let callback = renderRoot(root, Sync, true);304 while (callback !== null) {305 callback = callback(true);306 }307 } else {308 scheduleCallbackForRoot(root, ImmediatePriority, Sync);309 if (workPhase === NotWorking) {310 // Flush the synchronous work now, wnless we're already working or inside311 // a batch. This is intentionally inside scheduleUpdateOnFiber instead of312 // scheduleCallbackForFiber to preserve the ability to schedule a callback313 // without immediately flushing it. We only do this for user-initated314 // updates, to preserve historical behavior of sync mode.315 flushImmediateQueue();316 }317 }318 } else {319 // TODO: computeExpirationForFiber also reads the priority. Pass the320 // priority as an argument to that function and this one.321 const priorityLevel = getCurrentPriorityLevel();322 if (priorityLevel === UserBlockingPriority) {323 // This is the result of a discrete event. Track the lowest priority324 // discrete update per root so we can flush them early, if needed.325 if (rootsWithPendingDiscreteUpdates === null) {326 rootsWithPendingDiscreteUpdates = new Map([[root, expirationTime]]);327 } else {328 const lastDiscreteTime = rootsWithPendingDiscreteUpdates.get(root);329 if (330 lastDiscreteTime === undefined ||331 lastDiscreteTime > expirationTime332 ) {333 rootsWithPendingDiscreteUpdates.set(root, expirationTime);334 }335 }336 }337 scheduleCallbackForRoot(root, priorityLevel, expirationTime);338 }339}340export const scheduleWork = scheduleUpdateOnFiber;341// This is split into a separate function so we can mark a fiber with pending342// work without treating it as a typical update that originates from an event;343// e.g. retrying a Suspense boundary isn't an update, but it does schedule work344// on a fiber.345function markUpdateTimeFromFiberToRoot(fiber, expirationTime) {346 // Update the source fiber's expiration time347 if (fiber.expirationTime < expirationTime) {348 fiber.expirationTime = expirationTime;349 }350 let alternate = fiber.alternate;351 if (alternate !== null && alternate.expirationTime < expirationTime) {352 alternate.expirationTime = expirationTime;353 }354 // Walk the parent path to the root and update the child expiration time.355 let node = fiber.return;356 let root = null;357 if (node === null && fiber.tag === HostRoot) {358 root = fiber.stateNode;359 } else {360 while (node !== null) {361 alternate = node.alternate;362 if (node.childExpirationTime < expirationTime) {363 node.childExpirationTime = expirationTime;364 if (365 alternate !== null &&366 alternate.childExpirationTime < expirationTime367 ) {368 alternate.childExpirationTime = expirationTime;369 }370 } else if (371 alternate !== null &&372 alternate.childExpirationTime < expirationTime373 ) {374 alternate.childExpirationTime = expirationTime;375 }376 if (node.return === null && node.tag === HostRoot) {377 root = node.stateNode;378 break;379 }380 node = node.return;381 }382 }383 if (root !== null) {384 // Update the first and last pending expiration times in this root385 const firstPendingTime = root.firstPendingTime;386 if (expirationTime > firstPendingTime) {387 root.firstPendingTime = expirationTime;388 }389 const lastPendingTime = root.lastPendingTime;390 if (lastPendingTime === NoWork || expirationTime < lastPendingTime) {391 root.lastPendingTime = expirationTime;392 }393 }394 return root;395}396// Use this function, along with runRootCallback, to ensure that only a single397// callback per root is scheduled. It's still possible to call renderRoot398// directly, but scheduling via this function helps avoid excessive callbacks.399// It works by storing the callback node and expiration time on the root. When a400// new callback comes in, it compares the expiration time to determine if it401// should cancel the previous one. It also relies on commitRoot scheduling a402// callback to render the next level, because that means we don't need a403// separate callback per expiration time.404function scheduleCallbackForRoot(405 root: FiberRoot,406 priorityLevel: ReactPriorityLevel,407 expirationTime: ExpirationTime,408) {409 const existingCallbackExpirationTime = root.callbackExpirationTime;410 if (existingCallbackExpirationTime < expirationTime) {411 // New callback has higher priority than the existing one.412 const existingCallbackNode = root.callbackNode;413 if (existingCallbackNode !== null) {414 cancelCallback(existingCallbackNode);415 }416 root.callbackExpirationTime = expirationTime;417 let options = null;418 if (expirationTime !== Sync && expirationTime !== Never) {419 let timeout = expirationTimeToMs(expirationTime) - now();420 if (timeout > 5000) {421 // Sanity check. Should never take longer than 5 seconds.422 // TODO: Add internal warning?423 timeout = 5000;424 }425 options = {timeout};426 }427 root.callbackNode = scheduleCallback(428 priorityLevel,429 runRootCallback.bind(430 null,431 root,432 renderRoot.bind(null, root, expirationTime),433 ),434 options,435 );436 if (437 enableUserTimingAPI &&438 expirationTime !== Sync &&439 workPhase !== RenderPhase &&440 workPhase !== CommitPhase441 ) {442 // Scheduled an async callback, and we're not already working. Add an443 // entry to the flamegraph that shows we're waiting for a callback444 // to fire.445 startRequestCallbackTimer();446 }447 }448 // Add the current set of interactions to the pending set associated with449 // this root.450 schedulePendingInteraction(root, expirationTime);451}452function runRootCallback(root, callback, isSync) {453 const prevCallbackNode = root.callbackNode;454 let continuation = null;455 try {456 continuation = callback(isSync);457 if (continuation !== null) {458 return runRootCallback.bind(null, root, continuation);459 } else {460 return null;461 }462 } finally {463 // If the callback exits without returning a continuation, remove the464 // corresponding callback node from the root. Unless the callback node465 // has changed, which implies that it was already cancelled by a high466 // priority update.467 if (continuation === null && prevCallbackNode === root.callbackNode) {468 root.callbackNode = null;469 root.callbackExpirationTime = NoWork;470 }471 }472}473export function flushRoot(root: FiberRoot, expirationTime: ExpirationTime) {474 if (workPhase === RenderPhase || workPhase === CommitPhase) {475 invariant(476 false,477 'work.commit(): Cannot commit while already rendering. This likely ' +478 'means you attempted to commit from inside a lifecycle method.',479 );480 }481 scheduleCallback(482 ImmediatePriority,483 renderRoot.bind(null, root, expirationTime),484 );485 flushImmediateQueue();486}487export function flushInteractiveUpdates() {488 if (workPhase === RenderPhase || workPhase === CommitPhase) {489 // Can't synchronously flush interactive updates if React is already490 // working. This is currently a no-op.491 // TODO: Should we fire a warning? This happens if you synchronously invoke492 // an input event inside an effect, like with `element.click()`.493 return;494 }495 flushPendingDiscreteUpdates();496}497function resolveLocksOnRoot(root: FiberRoot, expirationTime: ExpirationTime) {498 const firstBatch = root.firstBatch;499 if (500 firstBatch !== null &&501 firstBatch._defer &&502 firstBatch._expirationTime >= expirationTime503 ) {504 root.finishedWork = root.current.alternate;505 root.pendingCommitExpirationTime = expirationTime;506 scheduleCallback(NormalPriority, () => {507 firstBatch._onComplete();508 return null;509 });510 return true;511 } else {512 return false;513 }514}515export function deferredUpdates<A>(fn: () => A): A {516 // TODO: Remove in favor of Scheduler.next517 return runWithPriority(NormalPriority, fn);518}519export function interactiveUpdates<A, B, C, R>(520 fn: (A, B, C) => R,521 a: A,522 b: B,523 c: C,524): R {525 if (workPhase === NotWorking) {526 // TODO: Remove this call. Instead of doing this automatically, the caller527 // should explicitly call flushInteractiveUpdates.528 flushPendingDiscreteUpdates();529 }530 return runWithPriority(UserBlockingPriority, fn.bind(null, a, b, c));531}532export function syncUpdates<A, B, C, R>(533 fn: (A, B, C) => R,534 a: A,535 b: B,536 c: C,537): R {538 return runWithPriority(ImmediatePriority, fn.bind(null, a, b, c));539}540function flushPendingDiscreteUpdates() {541 if (rootsWithPendingDiscreteUpdates !== null) {542 // For each root with pending discrete updates, schedule a callback to543 // immediately flush them.544 const roots = rootsWithPendingDiscreteUpdates;545 rootsWithPendingDiscreteUpdates = null;546 roots.forEach((expirationTime, root) => {547 scheduleCallback(548 ImmediatePriority,549 renderRoot.bind(null, root, expirationTime),550 );551 });552 // Now flush the immediate queue.553 flushImmediateQueue();554 }555}556export function batchedUpdates<A, R>(fn: A => R, a: A): R {557 if (workPhase !== NotWorking) {558 // We're already working, or inside a batch, so batchedUpdates is a no-op.559 return fn(a);560 }561 workPhase = BatchedPhase;562 try {563 return fn(a);564 } finally {565 workPhase = NotWorking;566 // Flush the immediate callbacks that were scheduled during this batch567 flushImmediateQueue();568 }569}570export function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {571 if (workPhase !== BatchedPhase && workPhase !== FlushSyncPhase) {572 // We're not inside batchedUpdates or flushSync, so unbatchedUpdates is573 // a no-op.574 return fn(a);575 }576 const prevWorkPhase = workPhase;577 workPhase = LegacyUnbatchedPhase;578 try {579 return fn(a);580 } finally {581 workPhase = prevWorkPhase;582 }583}584export function flushSync<A, R>(fn: A => R, a: A): R {585 if (workPhase === RenderPhase || workPhase === CommitPhase) {586 invariant(587 false,588 'flushSync was called from inside a lifecycle method. It cannot be ' +589 'called when React is already rendering.',590 );591 }592 const prevWorkPhase = workPhase;593 workPhase = FlushSyncPhase;594 try {595 return runWithPriority(ImmediatePriority, fn.bind(null, a));596 } finally {597 workPhase = prevWorkPhase;598 // Flush the immediate callbacks that were scheduled during this batch.599 // Note that this will happen even if batchedUpdates is higher up600 // the stack.601 flushImmediateQueue();602 }603}604export function flushControlled(fn: () => mixed): void {605 const prevWorkPhase = workPhase;606 workPhase = BatchedPhase;607 try {608 runWithPriority(ImmediatePriority, fn);609 } finally {610 workPhase = prevWorkPhase;611 if (workPhase === NotWorking) {612 // Flush the immediate callbacks that were scheduled during this batch613 flushImmediateQueue();614 }615 }616}617function prepareFreshStack(root, expirationTime) {618 root.pendingCommitExpirationTime = NoWork;619 const timeoutHandle = root.timeoutHandle;620 if (timeoutHandle !== noTimeout) {621 // The root previous suspended and scheduled a timeout to commit a fallback622 // state. Now that we have additional work, cancel the timeout.623 root.timeoutHandle = noTimeout;624 // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above625 cancelTimeout(timeoutHandle);626 }627 if (workInProgress !== null) {628 let interruptedWork = workInProgress.return;629 while (interruptedWork !== null) {630 unwindInterruptedWork(interruptedWork);631 interruptedWork = interruptedWork.return;632 }633 }634 workInProgressRoot = root;635 workInProgress = createWorkInProgress(root.current, null, expirationTime);636 renderExpirationTime = expirationTime;637 workInProgressRootExitStatus = RootIncomplete;638 workInProgressRootMostRecentEventTime = Sync;639 if (__DEV__) {640 ReactStrictModeWarnings.discardPendingWarnings();641 }642}643function renderRoot(644 root: FiberRoot,645 expirationTime: ExpirationTime,646 isSync: boolean,647): SchedulerCallback | null {648 invariant(649 workPhase !== RenderPhase && workPhase !== CommitPhase,650 'Should not already be working.',651 );652 if (enableUserTimingAPI && expirationTime !== Sync) {653 const didExpire = isSync;654 stopRequestCallbackTimer(didExpire);655 }656 if (root.firstPendingTime < expirationTime) {657 // If there's no work left at this expiration time, exit immediately. This658 // happens when multiple callbacks are scheduled for a single root, but an659 // earlier callback flushes the work of a later one.660 return null;661 }662 if (root.pendingCommitExpirationTime === expirationTime) {663 // There's already a pending commit at this expiration time.664 root.pendingCommitExpirationTime = NoWork;665 return commitRoot.bind(null, root, expirationTime);666 }667 flushPassiveEffects();668 // If the root or expiration time have changed, throw out the existing stack669 // and prepare a fresh one. Otherwise we'll continue where we left off.670 if (root !== workInProgressRoot || expirationTime !== renderExpirationTime) {671 prepareFreshStack(root, expirationTime);672 startWorkOnPendingInteraction(root, expirationTime);673 }674 // If we have a work-in-progress fiber, it means there's still work to do675 // in this root.676 if (workInProgress !== null) {677 const prevWorkPhase = workPhase;678 workPhase = RenderPhase;679 let prevDispatcher = ReactCurrentDispatcher.current;680 if (prevDispatcher === null) {681 // The React isomorphic package does not include a default dispatcher.682 // Instead the first renderer will lazily attach one, in order to give683 // nicer error messages.684 prevDispatcher = ContextOnlyDispatcher;685 }686 ReactCurrentDispatcher.current = ContextOnlyDispatcher;687 let prevInteractions: Set<Interaction> | null = null;688 if (enableSchedulerTracing) {689 prevInteractions = __interactionsRef.current;690 __interactionsRef.current = root.memoizedInteractions;691 }692 startWorkLoopTimer(workInProgress);693 // TODO: Fork renderRoot into renderRootSync and renderRootAsync694 if (isSync) {695 if (expirationTime !== Sync) {696 // An async update expired. There may be other expired updates on697 // this root. We should render all the expired work in a698 // single batch.699 const currentTime = requestCurrentTime();700 if (currentTime < expirationTime) {701 // Restart at the current time.702 workPhase = prevWorkPhase;703 resetContextDependencies();704 ReactCurrentDispatcher.current = prevDispatcher;705 if (enableSchedulerTracing) {706 __interactionsRef.current = ((prevInteractions: any): Set<707 Interaction,708 >);709 }710 return renderRoot.bind(null, root, currentTime);711 }712 }713 } else {714 // Since we know we're in a React event, we can clear the current715 // event time. The next update will compute a new event time.716 currentEventTime = NoWork;717 }718 do {719 try {720 if (isSync) {721 workLoopSync();722 } else {723 workLoop();724 }725 break;726 } catch (thrownValue) {727 // Reset module-level state that was set during the render phase.728 resetContextDependencies();729 resetHooks();730 const sourceFiber = workInProgress;731 if (sourceFiber === null || sourceFiber.return === null) {732 // Expected to be working on a non-root fiber. This is a fatal error733 // because there's no ancestor that can handle it; the root is734 // supposed to capture all errors that weren't caught by an error735 // boundary.736 prepareFreshStack(root, expirationTime);737 workPhase = prevWorkPhase;738 throw thrownValue;739 }740 if (enableProfilerTimer && sourceFiber.mode & ProfileMode) {741 // Record the time spent rendering before an error was thrown. This742 // avoids inaccurate Profiler durations in the case of a743 // suspended render.744 stopProfilerTimerIfRunningAndRecordDelta(sourceFiber, true);745 }746 const returnFiber = sourceFiber.return;747 throwException(748 root,749 returnFiber,750 sourceFiber,751 thrownValue,752 renderExpirationTime,753 );754 workInProgress = completeUnitOfWork(sourceFiber);755 }756 } while (true);757 workPhase = prevWorkPhase;758 resetContextDependencies();759 ReactCurrentDispatcher.current = prevDispatcher;760 if (enableSchedulerTracing) {761 __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);762 }763 if (workInProgress !== null) {764 // There's still work left over. Return a continuation.765 stopInterruptedWorkLoopTimer();766 if (expirationTime !== Sync) {767 startRequestCallbackTimer();768 }769 return renderRoot.bind(null, root, expirationTime);770 }771 }772 // We now have a consistent tree. The next step is either to commit it, or, if773 // something suspended, wait to commit it after a timeout.774 stopFinishedWorkLoopTimer();775 const isLocked = resolveLocksOnRoot(root, expirationTime);776 if (isLocked) {777 // This root has a lock that prevents it from committing. Exit. If we begin778 // work on the root again, without any intervening updates, it will finish779 // without doing additional work.780 return null;781 }782 // Set this to null to indicate there's no in-progress render.783 workInProgressRoot = null;784 switch (workInProgressRootExitStatus) {785 case RootIncomplete: {786 invariant(false, 'Should have a work-in-progress.');787 }788 // Flow knows about invariant, so it compains if I add a break statement,789 // but eslint doesn't know about invariant, so it complains if I do.790 // eslint-disable-next-line no-fallthrough791 case RootErrored: {792 // An error was thrown. First check if there is lower priority work793 // scheduled on this root.794 const lastPendingTime = root.lastPendingTime;795 if (root.lastPendingTime < expirationTime) {796 // There's lower priority work. Before raising the error, try rendering797 // at the lower priority to see if it fixes it. Use a continuation to798 // maintain the existing priority and position in the queue.799 return renderRoot.bind(null, root, lastPendingTime);800 }801 if (!isSync) {802 // If we're rendering asynchronously, it's possible the error was803 // caused by tearing due to a mutation during an event. Try rendering804 // one more time without yiedling to events.805 prepareFreshStack(root, expirationTime);806 scheduleCallback(807 ImmediatePriority,808 renderRoot.bind(null, root, expirationTime),809 );810 return null;811 }812 // If we're already rendering synchronously, commit the root in its813 // errored state.814 return commitRoot.bind(null, root, expirationTime);815 }816 case RootSuspended: {817 if (!isSync) {818 const lastPendingTime = root.lastPendingTime;819 if (root.lastPendingTime < expirationTime) {820 // There's lower priority work. It might be unsuspended. Try rendering821 // at that level.822 return renderRoot.bind(null, root, lastPendingTime);823 }824 // If workInProgressRootMostRecentEventTime is Sync, that means we didn't825 // track any event times. That can happen if we retried but nothing switched826 // from fallback to content. There's no reason to delay doing no work.827 if (workInProgressRootMostRecentEventTime !== Sync) {828 let msUntilTimeout = computeMsUntilTimeout(829 workInProgressRootMostRecentEventTime,830 expirationTime,831 );832 // Don't bother with a very short suspense time.833 if (msUntilTimeout > 10) {834 // The render is suspended, it hasn't timed out, and there's no lower835 // priority work to do. Instead of committing the fallback836 // immediately, wait for more data to arrive.837 root.timeoutHandle = scheduleTimeout(838 commitRoot.bind(null, root, expirationTime),839 msUntilTimeout,840 );841 return null;842 }843 }844 }845 // The work expired. Commit immediately.846 return commitRoot.bind(null, root, expirationTime);847 }848 case RootCompleted: {849 // The work completed. Ready to commit.850 return commitRoot.bind(null, root, expirationTime);851 }852 default: {853 invariant(false, 'Unknown root exit status.');854 }855 }856}857export function markRenderEventTime(expirationTime: ExpirationTime): void {858 if (expirationTime < workInProgressRootMostRecentEventTime) {859 workInProgressRootMostRecentEventTime = expirationTime;860 }861}862export function renderDidSuspend(): void {863 if (workInProgressRootExitStatus === RootIncomplete) {864 workInProgressRootExitStatus = RootSuspended;865 }866}867export function renderDidError() {868 if (869 workInProgressRootExitStatus === RootIncomplete ||870 workInProgressRootExitStatus === RootSuspended871 ) {872 workInProgressRootExitStatus = RootErrored;873 }874}875function inferTimeFromExpirationTime(expirationTime: ExpirationTime): number {876 // We don't know exactly when the update was scheduled, but we can infer an877 // approximate start time from the expiration time.878 const earliestExpirationTimeMs = expirationTimeToMs(expirationTime);879 return earliestExpirationTimeMs - LOW_PRIORITY_EXPIRATION;880}881function workLoopSync() {882 // Already timed out, so perform work without checking if we need to yield.883 while (workInProgress !== null) {884 workInProgress = performUnitOfWork(workInProgress);885 }886}887function workLoop() {888 // Perform work until Scheduler asks us to yield889 while (workInProgress !== null && !shouldYield()) {890 workInProgress = performUnitOfWork(workInProgress);891 }892}893function performUnitOfWork(unitOfWork: Fiber): Fiber | null {894 // The current, flushed, state of this fiber is the alternate. Ideally895 // nothing should rely on this, but relying on it here means that we don't896 // need an additional field on the work in progress.897 const current = unitOfWork.alternate;898 startWorkTimer(unitOfWork);899 setCurrentDebugFiberInDEV(unitOfWork);900 let next;901 if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoContext) {902 startProfilerTimer(unitOfWork);903 next = beginWork(current, unitOfWork, renderExpirationTime);904 stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);905 } else {906 next = beginWork(current, unitOfWork, renderExpirationTime);907 }908 resetCurrentDebugFiberInDEV();909 unitOfWork.memoizedProps = unitOfWork.pendingProps;910 if (next === null) {911 // If this doesn't spawn new work, complete the current work.912 next = completeUnitOfWork(unitOfWork);913 }914 ReactCurrentOwner.current = null;915 return next;916}917function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {918 // Attempt to complete the current unit of work, then move to the next919 // sibling. If there are no more siblings, return to the parent fiber.920 workInProgress = unitOfWork;921 do {922 // The current, flushed, state of this fiber is the alternate. Ideally923 // nothing should rely on this, but relying on it here means that we don't924 // need an additional field on the work in progress.925 const current = workInProgress.alternate;926 const returnFiber = workInProgress.return;927 // Check if the work completed or if something threw.928 if ((workInProgress.effectTag & Incomplete) === NoEffect) {929 setCurrentDebugFiberInDEV(workInProgress);930 let next;931 if (932 !enableProfilerTimer ||933 (workInProgress.mode & ProfileMode) === NoContext934 ) {935 next = completeWork(current, workInProgress, renderExpirationTime);936 } else {937 startProfilerTimer(workInProgress);938 next = completeWork(current, workInProgress, renderExpirationTime);939 // Update render duration assuming we didn't error.940 stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);941 }942 stopWorkTimer(workInProgress);943 resetCurrentDebugFiberInDEV();944 resetChildExpirationTime(workInProgress);945 if (next !== null) {946 // Completing this fiber spawned new work. Work on that next.947 return next;948 }949 if (950 returnFiber !== null &&951 // Do not append effects to parents if a sibling failed to complete952 (returnFiber.effectTag & Incomplete) === NoEffect953 ) {954 // Append all the effects of the subtree and this fiber onto the effect955 // list of the parent. The completion order of the children affects the956 // side-effect order.957 if (returnFiber.firstEffect === null) {958 returnFiber.firstEffect = workInProgress.firstEffect;959 }960 if (workInProgress.lastEffect !== null) {961 if (returnFiber.lastEffect !== null) {962 returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;963 }964 returnFiber.lastEffect = workInProgress.lastEffect;965 }966 // If this fiber had side-effects, we append it AFTER the children's967 // side-effects. We can perform certain side-effects earlier if needed,968 // by doing multiple passes over the effect list. We don't want to969 // schedule our own side-effect on our own list because if end up970 // reusing children we'll schedule this effect onto itself since we're971 // at the end.972 const effectTag = workInProgress.effectTag;973 // Skip both NoWork and PerformedWork tags when creating the effect974 // list. PerformedWork effect is read by React DevTools but shouldn't be975 // committed.976 if (effectTag > PerformedWork) {977 if (returnFiber.lastEffect !== null) {978 returnFiber.lastEffect.nextEffect = workInProgress;979 } else {980 returnFiber.firstEffect = workInProgress;981 }982 returnFiber.lastEffect = workInProgress;983 }984 }985 } else {986 // This fiber did not complete because something threw. Pop values off987 // the stack without entering the complete phase. If this is a boundary,988 // capture values if possible.989 const next = unwindWork(workInProgress, renderExpirationTime);990 // Because this fiber did not complete, don't reset its expiration time.991 if (992 enableProfilerTimer &&993 (workInProgress.mode & ProfileMode) !== NoContext994 ) {995 // Record the render duration for the fiber that errored.996 stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);997 // Include the time spent working on failed children before continuing.998 let actualDuration = workInProgress.actualDuration;999 let child = workInProgress.child;1000 while (child !== null) {1001 actualDuration += child.actualDuration;1002 child = child.sibling;1003 }1004 workInProgress.actualDuration = actualDuration;1005 }1006 if (next !== null) {1007 // If completing this work spawned new work, do that next. We'll come1008 // back here again.1009 // Since we're restarting, remove anything that is not a host effect1010 // from the effect tag.1011 // TODO: The name stopFailedWorkTimer is misleading because Suspense1012 // also captures and restarts.1013 stopFailedWorkTimer(workInProgress);1014 next.effectTag &= HostEffectMask;1015 return next;1016 }1017 stopWorkTimer(workInProgress);1018 if (returnFiber !== null) {1019 // Mark the parent fiber as incomplete and clear its effect list.1020 returnFiber.firstEffect = returnFiber.lastEffect = null;1021 returnFiber.effectTag |= Incomplete;1022 }1023 }1024 const siblingFiber = workInProgress.sibling;1025 if (siblingFiber !== null) {1026 // If there is more work to do in this returnFiber, do that next.1027 return siblingFiber;1028 }1029 // Otherwise, return to the parent1030 workInProgress = returnFiber;1031 } while (workInProgress !== null);1032 // We've reached the root.1033 if (workInProgressRootExitStatus === RootIncomplete) {1034 workInProgressRootExitStatus = RootCompleted;1035 }1036 return null;1037}1038function resetChildExpirationTime(completedWork: Fiber) {1039 if (1040 renderExpirationTime !== Never &&1041 completedWork.childExpirationTime === Never1042 ) {1043 // The children of this component are hidden. Don't bubble their1044 // expiration times.1045 return;1046 }1047 let newChildExpirationTime = NoWork;1048 // Bubble up the earliest expiration time.1049 if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoContext) {1050 // In profiling mode, resetChildExpirationTime is also used to reset1051 // profiler durations.1052 let actualDuration = completedWork.actualDuration;1053 let treeBaseDuration = completedWork.selfBaseDuration;1054 // When a fiber is cloned, its actualDuration is reset to 0. This value will1055 // only be updated if work is done on the fiber (i.e. it doesn't bailout).1056 // When work is done, it should bubble to the parent's actualDuration. If1057 // the fiber has not been cloned though, (meaning no work was done), then1058 // this value will reflect the amount of time spent working on a previous1059 // render. In that case it should not bubble. We determine whether it was1060 // cloned by comparing the child pointer.1061 const shouldBubbleActualDurations =1062 completedWork.alternate === null ||1063 completedWork.child !== completedWork.alternate.child;1064 let child = completedWork.child;1065 while (child !== null) {1066 const childUpdateExpirationTime = child.expirationTime;1067 const childChildExpirationTime = child.childExpirationTime;1068 if (childUpdateExpirationTime > newChildExpirationTime) {1069 newChildExpirationTime = childUpdateExpirationTime;1070 }1071 if (childChildExpirationTime > newChildExpirationTime) {1072 newChildExpirationTime = childChildExpirationTime;1073 }1074 if (shouldBubbleActualDurations) {1075 actualDuration += child.actualDuration;1076 }1077 treeBaseDuration += child.treeBaseDuration;1078 child = child.sibling;1079 }1080 completedWork.actualDuration = actualDuration;1081 completedWork.treeBaseDuration = treeBaseDuration;1082 } else {1083 let child = completedWork.child;1084 while (child !== null) {1085 const childUpdateExpirationTime = child.expirationTime;1086 const childChildExpirationTime = child.childExpirationTime;1087 if (childUpdateExpirationTime > newChildExpirationTime) {1088 newChildExpirationTime = childUpdateExpirationTime;1089 }1090 if (childChildExpirationTime > newChildExpirationTime) {1091 newChildExpirationTime = childChildExpirationTime;1092 }1093 child = child.sibling;1094 }1095 }1096 completedWork.childExpirationTime = newChildExpirationTime;1097}1098function commitRoot(root, expirationTime) {1099 runWithPriority(1100 ImmediatePriority,1101 commitRootImpl.bind(null, root, expirationTime),1102 );1103 // If there are passive effects, schedule a callback to flush them. This goes1104 // outside commitRootImpl so that it inherits the priority of the render.1105 if (rootWithPendingPassiveEffects !== null) {1106 const priorityLevel = getCurrentPriorityLevel();1107 scheduleCallback(priorityLevel, () => {1108 flushPassiveEffects();1109 return null;1110 });1111 }1112 return null;1113}1114function commitRootImpl(root, expirationTime) {1115 flushPassiveEffects();1116 flushRenderPhaseStrictModeWarningsInDEV();1117 invariant(1118 workPhase !== RenderPhase && workPhase !== CommitPhase,1119 'Should not already be working.',1120 );1121 const finishedWork = root.current.alternate;1122 invariant(finishedWork !== null, 'Should have a work-in-progress root.');1123 // commitRoot never returns a continuation; it always finishes synchronously.1124 // So we can clear these now to allow a new callback to be scheduled.1125 root.callbackNode = null;1126 root.callbackExpirationTime = NoWork;1127 startCommitTimer();1128 // Update the first and last pending times on this root. The new first1129 // pending time is whatever is left on the root fiber.1130 const updateExpirationTimeBeforeCommit = finishedWork.expirationTime;1131 const childExpirationTimeBeforeCommit = finishedWork.childExpirationTime;1132 const firstPendingTimeBeforeCommit =1133 childExpirationTimeBeforeCommit > updateExpirationTimeBeforeCommit1134 ? childExpirationTimeBeforeCommit1135 : updateExpirationTimeBeforeCommit;1136 root.firstPendingTime = firstPendingTimeBeforeCommit;1137 if (firstPendingTimeBeforeCommit < root.lastPendingTime) {1138 // This usually means we've finished all the work, but it can also happen1139 // when something gets downprioritized during render, like a hidden tree.1140 root.lastPendingTime = firstPendingTimeBeforeCommit;1141 }1142 if (root === workInProgressRoot) {1143 // We can reset these now that they are finished.1144 workInProgressRoot = null;1145 workInProgress = null;1146 renderExpirationTime = NoWork;1147 } else {1148 // This indicates that the last root we worked on is not the same one that1149 // we're committing now. This most commonly happens when a suspended root1150 // times out.1151 }1152 // Get the list of effects.1153 let firstEffect;1154 if (finishedWork.effectTag > PerformedWork) {1155 // A fiber's effect list consists only of its children, not itself. So if1156 // the root has an effect, we need to add it to the end of the list. The1157 // resulting list is the set that would belong to the root's parent, if it1158 // had one; that is, all the effects in the tree including the root.1159 if (finishedWork.lastEffect !== null) {1160 finishedWork.lastEffect.nextEffect = finishedWork;1161 firstEffect = finishedWork.firstEffect;1162 } else {1163 firstEffect = finishedWork;1164 }1165 } else {1166 // There is no effect on the root.1167 firstEffect = finishedWork.firstEffect;1168 }1169 if (firstEffect !== null) {1170 const prevWorkPhase = workPhase;1171 workPhase = CommitPhase;1172 let prevInteractions: Set<Interaction> | null = null;1173 if (enableSchedulerTracing) {1174 prevInteractions = __interactionsRef.current;1175 __interactionsRef.current = root.memoizedInteractions;1176 }1177 // Reset this to null before calling lifecycles1178 ReactCurrentOwner.current = null;1179 // The commit phase is broken into several sub-phases. We do a separate pass1180 // of the effect list for each phase: all mutation effects come before all1181 // layout effects, and so on.1182 // The first phase a "before mutation" phase. We use this phase to read the1183 // state of the host tree right before we mutate it. This is where1184 // getSnapshotBeforeUpdate is called.1185 startCommitSnapshotEffectsTimer();1186 prepareForCommit(root.containerInfo);1187 nextEffect = firstEffect;1188 do {1189 if (__DEV__) {1190 invokeGuardedCallback(null, commitBeforeMutationEffects, null);1191 if (hasCaughtError()) {1192 invariant(nextEffect !== null, 'Should be working on an effect.');1193 const error = clearCaughtError();1194 captureCommitPhaseError(nextEffect, error);1195 nextEffect = nextEffect.nextEffect;1196 }1197 } else {1198 try {1199 commitBeforeMutationEffects();1200 } catch (error) {1201 invariant(nextEffect !== null, 'Should be working on an effect.');1202 captureCommitPhaseError(nextEffect, error);1203 nextEffect = nextEffect.nextEffect;1204 }1205 }1206 } while (nextEffect !== null);1207 stopCommitSnapshotEffectsTimer();1208 if (enableProfilerTimer) {1209 // Mark the current commit time to be shared by all Profilers in this1210 // batch. This enables them to be grouped later.1211 recordCommitTime();1212 }1213 // The next phase is the mutation phase, where we mutate the host tree.1214 startCommitHostEffectsTimer();1215 nextEffect = firstEffect;1216 do {1217 if (__DEV__) {1218 invokeGuardedCallback(null, commitMutationEffects, null);1219 if (hasCaughtError()) {1220 invariant(nextEffect !== null, 'Should be working on an effect.');1221 const error = clearCaughtError();1222 captureCommitPhaseError(nextEffect, error);1223 nextEffect = nextEffect.nextEffect;1224 }1225 } else {1226 try {1227 commitMutationEffects();1228 } catch (error) {1229 invariant(nextEffect !== null, 'Should be working on an effect.');1230 captureCommitPhaseError(nextEffect, error);1231 nextEffect = nextEffect.nextEffect;1232 }1233 }1234 } while (nextEffect !== null);1235 stopCommitHostEffectsTimer();1236 resetAfterCommit(root.containerInfo);1237 // The work-in-progress tree is now the current tree. This must come after1238 // the mutation phase, so that the previous tree is still current during1239 // componentWillUnmount, but before the layout phase, so that the finished1240 // work is current during componentDidMount/Update.1241 root.current = finishedWork;1242 // The next phase is the layout phase, where we call effects that read1243 // the host tree after it's been mutated. The idiomatic use case for this is1244 // layout, but class component lifecycles also fire here for legacy reasons.1245 startCommitLifeCyclesTimer();1246 nextEffect = firstEffect;1247 do {1248 if (__DEV__) {1249 invokeGuardedCallback(1250 null,1251 commitLayoutEffects,1252 null,1253 root,1254 expirationTime,1255 );1256 if (hasCaughtError()) {1257 invariant(nextEffect !== null, 'Should be working on an effect.');1258 const error = clearCaughtError();1259 captureCommitPhaseError(nextEffect, error);1260 nextEffect = nextEffect.nextEffect;1261 }1262 } else {1263 try {1264 commitLayoutEffects(root, expirationTime);1265 } catch (error) {1266 invariant(nextEffect !== null, 'Should be working on an effect.');1267 captureCommitPhaseError(nextEffect, error);1268 nextEffect = nextEffect.nextEffect;1269 }1270 }1271 } while (nextEffect !== null);1272 stopCommitLifeCyclesTimer();1273 nextEffect = null;1274 if (enableSchedulerTracing) {1275 __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1276 }1277 workPhase = prevWorkPhase;1278 } else {1279 // No effects.1280 root.current = finishedWork;1281 // Measure these anyway so the flamegraph explicitly shows that there were1282 // no effects.1283 // TODO: Maybe there's a better way to report this.1284 startCommitSnapshotEffectsTimer();1285 stopCommitSnapshotEffectsTimer();1286 if (enableProfilerTimer) {1287 recordCommitTime();1288 }1289 startCommitHostEffectsTimer();1290 stopCommitHostEffectsTimer();1291 startCommitLifeCyclesTimer();1292 stopCommitLifeCyclesTimer();1293 }1294 stopCommitTimer();1295 if (rootDoesHavePassiveEffects) {1296 // This commit has passive effects. Stash a reference to them. But don't1297 // schedule a callback until after flushing layout work.1298 rootDoesHavePassiveEffects = false;1299 rootWithPendingPassiveEffects = root;1300 pendingPassiveEffectsExpirationTime = expirationTime;1301 } else {1302 if (enableSchedulerTracing) {1303 // If there are no passive effects, then we can complete the pending1304 // interactions. Otherwise, we'll wait until after the passive effects1305 // are flushed.1306 finishPendingInteractions(root, expirationTime);1307 }1308 }1309 // Check if there's remaining work on this root1310 const remainingExpirationTime = root.firstPendingTime;1311 if (remainingExpirationTime !== NoWork) {1312 const currentTime = requestCurrentTime();1313 const priorityLevel = inferPriorityFromExpirationTime(1314 currentTime,1315 remainingExpirationTime,1316 );1317 scheduleCallbackForRoot(root, priorityLevel, remainingExpirationTime);1318 } else {1319 // If there's no remaining work, we can clear the set of already failed1320 // error boundaries.1321 legacyErrorBoundariesThatAlreadyFailed = null;1322 }1323 onCommitRoot(finishedWork.stateNode);1324 if (remainingExpirationTime === Sync) {1325 // Count the number of times the root synchronously re-renders without1326 // finishing. If there are too many, it indicates an infinite update loop.1327 if (root === rootWithNestedUpdates) {1328 nestedUpdateCount++;1329 } else {1330 nestedUpdateCount = 0;1331 rootWithNestedUpdates = root;1332 }1333 } else {1334 nestedUpdateCount = 0;1335 }1336 if (hasUncaughtError) {1337 hasUncaughtError = false;1338 const error = firstUncaughtError;1339 firstUncaughtError = null;1340 throw error;1341 }1342 if (workPhase === LegacyUnbatchedPhase) {1343 // This is a legacy edge case. We just committed the initial mount of1344 // a ReactDOM.render-ed root inside of batchedUpdates. The commit fired1345 // synchronously, but layout updates should be deferred until the end1346 // of the batch.1347 return null;1348 }1349 // If layout work was scheduled, flush it now.1350 flushImmediateQueue();1351 return null;1352}1353function commitBeforeMutationEffects() {1354 while (nextEffect !== null) {1355 if ((nextEffect.effectTag & Snapshot) !== NoEffect) {1356 setCurrentDebugFiberInDEV(nextEffect);1357 recordEffect();1358 const current = nextEffect.alternate;1359 commitBeforeMutationEffectOnFiber(current, nextEffect);1360 resetCurrentDebugFiberInDEV();1361 }1362 nextEffect = nextEffect.nextEffect;1363 }1364}1365function commitMutationEffects() {1366 // TODO: Should probably move the bulk of this function to commitWork.1367 while (nextEffect !== null) {1368 setCurrentDebugFiberInDEV(nextEffect);1369 const effectTag = nextEffect.effectTag;1370 if (effectTag & ContentReset) {1371 commitResetTextContent(nextEffect);1372 }1373 if (effectTag & Ref) {1374 const current = nextEffect.alternate;1375 if (current !== null) {1376 commitDetachRef(current);1377 }1378 }1379 // The following switch statement is only concerned about placement,1380 // updates, and deletions. To avoid needing to add a case for every possible1381 // bitmap value, we remove the secondary effects from the effect tag and1382 // switch on that value.1383 let primaryEffectTag = effectTag & (Placement | Update | Deletion);1384 switch (primaryEffectTag) {1385 case Placement: {1386 commitPlacement(nextEffect);1387 // Clear the "placement" from effect tag so that we know that this is1388 // inserted, before any life-cycles like componentDidMount gets called.1389 // TODO: findDOMNode doesn't rely on this any more but isMounted does1390 // and isMounted is deprecated anyway so we should be able to kill this.1391 nextEffect.effectTag &= ~Placement;1392 break;1393 }1394 case PlacementAndUpdate: {1395 // Placement1396 commitPlacement(nextEffect);1397 // Clear the "placement" from effect tag so that we know that this is1398 // inserted, before any life-cycles like componentDidMount gets called.1399 nextEffect.effectTag &= ~Placement;1400 // Update1401 const current = nextEffect.alternate;1402 commitWork(current, nextEffect);1403 break;1404 }1405 case Update: {1406 const current = nextEffect.alternate;1407 commitWork(current, nextEffect);1408 break;1409 }1410 case Deletion: {1411 commitDeletion(nextEffect);1412 break;1413 }1414 }1415 // TODO: Only record a mutation effect if primaryEffectTag is non-zero.1416 recordEffect();1417 resetCurrentDebugFiberInDEV();1418 nextEffect = nextEffect.nextEffect;1419 }1420}1421function commitLayoutEffects(1422 root: FiberRoot,1423 committedExpirationTime: ExpirationTime,1424) {1425 // TODO: Should probably move the bulk of this function to commitWork.1426 while (nextEffect !== null) {1427 setCurrentDebugFiberInDEV(nextEffect);1428 const effectTag = nextEffect.effectTag;1429 if (effectTag & (Update | Callback)) {1430 recordEffect();1431 const current = nextEffect.alternate;1432 commitLayoutEffectOnFiber(1433 root,1434 current,1435 nextEffect,1436 committedExpirationTime,1437 );1438 }1439 if (effectTag & Ref) {1440 recordEffect();1441 commitAttachRef(nextEffect);1442 }1443 if (effectTag & Passive) {1444 rootDoesHavePassiveEffects = true;1445 }1446 resetCurrentDebugFiberInDEV();1447 nextEffect = nextEffect.nextEffect;1448 }1449}1450export function flushPassiveEffects() {1451 if (rootWithPendingPassiveEffects === null) {1452 return false;1453 }1454 const root = rootWithPendingPassiveEffects;1455 const expirationTime = pendingPassiveEffectsExpirationTime;1456 rootWithPendingPassiveEffects = null;1457 pendingPassiveEffectsExpirationTime = NoWork;1458 let prevInteractions: Set<Interaction> | null = null;1459 if (enableSchedulerTracing) {1460 prevInteractions = __interactionsRef.current;1461 __interactionsRef.current = root.memoizedInteractions;1462 }1463 invariant(1464 workPhase !== RenderPhase && workPhase !== CommitPhase,1465 'Cannot flush passive effects while already rendering.',1466 );1467 const prevWorkPhase = workPhase;1468 workPhase = CommitPhase;1469 // Note: This currently assumes there are no passive effects on the root1470 // fiber, because the root is not part of its own effect list. This could1471 // change in the future.1472 let effect = root.current.firstEffect;1473 while (effect !== null) {1474 if (__DEV__) {1475 setCurrentDebugFiberInDEV(effect);1476 invokeGuardedCallback(null, commitPassiveHookEffects, null, effect);1477 if (hasCaughtError()) {1478 invariant(effect !== null, 'Should be working on an effect.');1479 const error = clearCaughtError();1480 captureCommitPhaseError(effect, error);1481 }1482 resetCurrentDebugFiberInDEV();1483 } else {1484 try {1485 commitPassiveHookEffects(effect);1486 } catch (error) {1487 invariant(effect !== null, 'Should be working on an effect.');1488 captureCommitPhaseError(effect, error);1489 }1490 }1491 effect = effect.nextEffect;1492 }1493 if (enableSchedulerTracing) {1494 __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1495 finishPendingInteractions(root, expirationTime);1496 }1497 workPhase = prevWorkPhase;1498 flushImmediateQueue();1499 // If additional passive effects were scheduled, increment a counter. If this1500 // exceeds the limit, we'll fire a warning.1501 nestedPassiveUpdateCount =1502 rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;1503 return true;1504}1505export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {1506 return (1507 legacyErrorBoundariesThatAlreadyFailed !== null &&1508 legacyErrorBoundariesThatAlreadyFailed.has(instance)1509 );1510}1511export function markLegacyErrorBoundaryAsFailed(instance: mixed) {1512 if (legacyErrorBoundariesThatAlreadyFailed === null) {1513 legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);1514 } else {1515 legacyErrorBoundariesThatAlreadyFailed.add(instance);1516 }1517}1518function prepareToThrowUncaughtError(error: mixed) {1519 if (!hasUncaughtError) {1520 hasUncaughtError = true;1521 firstUncaughtError = error;1522 }1523}1524export const onUncaughtError = prepareToThrowUncaughtError;1525function captureCommitPhaseErrorOnRoot(1526 rootFiber: Fiber,1527 sourceFiber: Fiber,1528 error: mixed,1529) {1530 const errorInfo = createCapturedValue(error, sourceFiber);1531 const update = createRootErrorUpdate(rootFiber, errorInfo, Sync);1532 enqueueUpdate(rootFiber, update);1533 const root = markUpdateTimeFromFiberToRoot(rootFiber, Sync);1534 if (root !== null) {1535 scheduleCallbackForRoot(root, ImmediatePriority, Sync);1536 }1537}1538export function captureCommitPhaseError(sourceFiber: Fiber, error: mixed) {1539 if (sourceFiber.tag === HostRoot) {1540 // Error was thrown at the root. There is no parent, so the root1541 // itself should capture it.1542 captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);1543 return;1544 }1545 let fiber = sourceFiber.return;1546 while (fiber !== null) {1547 if (fiber.tag === HostRoot) {1548 captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);1549 return;1550 } else if (fiber.tag === ClassComponent) {1551 const ctor = fiber.type;1552 const instance = fiber.stateNode;1553 if (1554 typeof ctor.getDerivedStateFromError === 'function' ||1555 (typeof instance.componentDidCatch === 'function' &&1556 !isAlreadyFailedLegacyErrorBoundary(instance))1557 ) {1558 const errorInfo = createCapturedValue(error, sourceFiber);1559 const update = createClassErrorUpdate(1560 fiber,1561 errorInfo,1562 // TODO: This is always sync1563 Sync,1564 );1565 enqueueUpdate(fiber, update);1566 const root = markUpdateTimeFromFiberToRoot(fiber, Sync);1567 if (root !== null) {1568 scheduleCallbackForRoot(root, ImmediatePriority, Sync);1569 }1570 return;1571 }1572 }1573 fiber = fiber.return;1574 }1575}1576export function pingSuspendedRoot(1577 root: FiberRoot,1578 thenable: Thenable,1579 suspendedTime: ExpirationTime,1580) {1581 const pingCache = root.pingCache;1582 if (pingCache !== null) {1583 // The thenable resolved, so we no longer need to memoize, because it will1584 // never be thrown again.1585 pingCache.delete(thenable);1586 }1587 if (workInProgressRoot === root && renderExpirationTime === suspendedTime) {1588 // Received a ping at the same priority level at which we're currently1589 // rendering. Restart from the root. Don't need to schedule a ping because1590 // we're already working on this tree.1591 prepareFreshStack(root, renderExpirationTime);1592 return;1593 }1594 const lastPendingTime = root.lastPendingTime;1595 if (lastPendingTime < suspendedTime) {1596 // The root is no longer suspended at this time.1597 return;1598 }1599 const pingTime = root.pingTime;1600 if (pingTime !== NoWork && pingTime < suspendedTime) {1601 // There's already a lower priority ping scheduled.1602 return;1603 }1604 // Mark the time at which this ping was scheduled.1605 root.pingTime = suspendedTime;1606 const currentTime = requestCurrentTime();1607 const priorityLevel = inferPriorityFromExpirationTime(1608 currentTime,1609 suspendedTime,1610 );1611 scheduleCallbackForRoot(root, priorityLevel, suspendedTime);1612}1613export function retryTimedOutBoundary(boundaryFiber: Fiber) {1614 // The boundary fiber (a Suspense component) previously timed out and was1615 // rendered in its fallback state. One of the promises that suspended it has1616 // resolved, which means at least part of the tree was likely unblocked. Try1617 // rendering again, at a new expiration time.1618 const currentTime = requestCurrentTime();1619 const retryTime = computeExpirationForFiber(currentTime, boundaryFiber);1620 // TODO: Special case idle priority?1621 const priorityLevel = inferPriorityFromExpirationTime(currentTime, retryTime);1622 const root = markUpdateTimeFromFiberToRoot(boundaryFiber, retryTime);1623 if (root !== null) {1624 scheduleCallbackForRoot(root, priorityLevel, retryTime);1625 }1626}1627export function resolveRetryThenable(boundaryFiber: Fiber, thenable: Thenable) {1628 let retryCache: WeakSet<Thenable> | Set<Thenable> | null;1629 if (enableSuspenseServerRenderer) {1630 switch (boundaryFiber.tag) {1631 case SuspenseComponent:1632 retryCache = boundaryFiber.stateNode;1633 break;1634 case DehydratedSuspenseComponent:1635 retryCache = boundaryFiber.memoizedState;1636 break;1637 default:1638 invariant(1639 false,1640 'Pinged unknown suspense boundary type. ' +1641 'This is probably a bug in React.',1642 );1643 }1644 } else {1645 retryCache = boundaryFiber.stateNode;1646 }1647 if (retryCache !== null) {1648 // The thenable resolved, so we no longer need to memoize, because it will1649 // never be thrown again.1650 retryCache.delete(thenable);1651 }1652 retryTimedOutBoundary(boundaryFiber);1653}1654// Computes the next Just Noticeable Difference (JND) boundary.1655// The theory is that a person can't tell the difference between small differences in time.1656// Therefore, if we wait a bit longer than necessary that won't translate to a noticeable1657// difference in the experience. However, waiting for longer might mean that we can avoid1658// showing an intermediate loading state. The longer we have already waited, the harder it1659// is to tell small differences in time. Therefore, the longer we've already waited,1660// the longer we can wait additionally. At some point we have to give up though.1661// We pick a train model where the next boundary commits at a consistent schedule.1662// These particular numbers are vague estimates. We expect to adjust them based on research.1663function jnd(timeElapsed: number) {1664 return timeElapsed < 1201665 ? 1201666 : timeElapsed < 4801667 ? 4801668 : timeElapsed < 10801669 ? 10801670 : timeElapsed < 19201671 ? 19201672 : timeElapsed < 30001673 ? 30001674 : timeElapsed < 43201675 ? 43201676 : ceil(timeElapsed / 1960) * 1960;1677}1678function computeMsUntilTimeout(1679 mostRecentEventTime: ExpirationTime,1680 committedExpirationTime: ExpirationTime,1681) {1682 if (disableYielding) {1683 // Timeout immediately when yielding is disabled.1684 return 0;1685 }1686 const eventTimeMs: number = inferTimeFromExpirationTime(mostRecentEventTime);1687 const currentTimeMs: number = now();1688 const timeElapsed = currentTimeMs - eventTimeMs;1689 let msUntilTimeout = jnd(timeElapsed) - timeElapsed;1690 // Compute the time until this render pass would expire.1691 const timeUntilExpirationMs =1692 expirationTimeToMs(committedExpirationTime) - currentTimeMs;1693 // Clamp the timeout to the expiration time.1694 // TODO: Once the event time is exact instead of inferred from expiration time1695 // we don't need this.1696 if (timeUntilExpirationMs < msUntilTimeout) {1697 msUntilTimeout = timeUntilExpirationMs;1698 }1699 // This is the value that is passed to `setTimeout`.1700 return msUntilTimeout;1701}1702function checkForNestedUpdates() {1703 if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {1704 nestedUpdateCount = 0;1705 rootWithNestedUpdates = null;1706 invariant(1707 false,1708 'Maximum update depth exceeded. This can happen when a component ' +1709 'repeatedly calls setState inside componentWillUpdate or ' +1710 'componentDidUpdate. React limits the number of nested updates to ' +1711 'prevent infinite loops.',1712 );1713 }1714 if (__DEV__) {1715 if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {1716 nestedPassiveUpdateCount = 0;...
ReactFiberScheduler.new.js
Source:ReactFiberScheduler.new.js
...279export function scheduleUpdateOnFiber(280 fiber: Fiber,281 expirationTime: ExpirationTime,282) {283 checkForNestedUpdates();284 warnAboutInvalidUpdatesOnClassComponentsInDEV(fiber);285 const root = markUpdateTimeFromFiberToRoot(fiber, expirationTime);286 if (root === null) {287 warnAboutUpdateOnUnmountedFiberInDEV(fiber);288 return;289 }290 root.pingTime = NoWork;291 checkForInterruption(fiber, expirationTime);292 recordScheduleUpdate();293 if (expirationTime === Sync) {294 if (workPhase === LegacyUnbatchedPhase) {295 // This is a legacy edge case. The initial mount of a ReactDOM.render-ed296 // root inside of batchedUpdates should be synchronous, but layout updates297 // should be deferred until the end of the batch.298 let callback = renderRoot(root, Sync, true);299 while (callback !== null) {300 callback = callback(true);301 }302 } else {303 scheduleCallbackForRoot(root, ImmediatePriority, Sync);304 if (workPhase === NotWorking) {305 // Flush the synchronous work now, wnless we're already working or inside306 // a batch. This is intentionally inside scheduleUpdateOnFiber instead of307 // scheduleCallbackForFiber to preserve the ability to schedule a callback308 // without immediately flushing it. We only do this for user-initated309 // updates, to preserve historical behavior of sync mode.310 flushImmediateQueue();311 }312 }313 } else {314 // TODO: computeExpirationForFiber also reads the priority. Pass the315 // priority as an argument to that function and this one.316 const priorityLevel = getCurrentPriorityLevel();317 if (priorityLevel === UserBlockingPriority) {318 // This is the result of a discrete event. Track the lowest priority319 // discrete update per root so we can flush them early, if needed.320 if (rootsWithPendingDiscreteUpdates === null) {321 rootsWithPendingDiscreteUpdates = new Map([[root, expirationTime]]);322 } else {323 const lastDiscreteTime = rootsWithPendingDiscreteUpdates.get(root);324 if (325 lastDiscreteTime === undefined ||326 lastDiscreteTime > expirationTime327 ) {328 rootsWithPendingDiscreteUpdates.set(root, expirationTime);329 }330 }331 }332 scheduleCallbackForRoot(root, priorityLevel, expirationTime);333 }334}335export const scheduleWork = scheduleUpdateOnFiber;336// This is split into a separate function so we can mark a fiber with pending337// work without treating it as a typical update that originates from an event;338// e.g. retrying a Suspense boundary isn't an update, but it does schedule work339// on a fiber.340function markUpdateTimeFromFiberToRoot(fiber, expirationTime) {341 // Update the source fiber's expiration time342 if (fiber.expirationTime < expirationTime) {343 fiber.expirationTime = expirationTime;344 }345 let alternate = fiber.alternate;346 if (alternate !== null && alternate.expirationTime < expirationTime) {347 alternate.expirationTime = expirationTime;348 }349 // Walk the parent path to the root and update the child expiration time.350 let node = fiber.return;351 let root = null;352 if (node === null && fiber.tag === HostRoot) {353 root = fiber.stateNode;354 } else {355 while (node !== null) {356 alternate = node.alternate;357 if (node.childExpirationTime < expirationTime) {358 node.childExpirationTime = expirationTime;359 if (360 alternate !== null &&361 alternate.childExpirationTime < expirationTime362 ) {363 alternate.childExpirationTime = expirationTime;364 }365 } else if (366 alternate !== null &&367 alternate.childExpirationTime < expirationTime368 ) {369 alternate.childExpirationTime = expirationTime;370 }371 if (node.return === null && node.tag === HostRoot) {372 root = node.stateNode;373 break;374 }375 node = node.return;376 }377 }378 if (root !== null) {379 // Update the first and last pending expiration times in this root380 const firstPendingTime = root.firstPendingTime;381 if (expirationTime > firstPendingTime) {382 root.firstPendingTime = expirationTime;383 }384 const lastPendingTime = root.lastPendingTime;385 if (lastPendingTime === NoWork || expirationTime < lastPendingTime) {386 root.lastPendingTime = expirationTime;387 }388 }389 return root;390}391// Use this function, along with runRootCallback, to ensure that only a single392// callback per root is scheduled. It's still possible to call renderRoot393// directly, but scheduling via this function helps avoid excessive callbacks.394// It works by storing the callback node and expiration time on the root. When a395// new callback comes in, it compares the expiration time to determine if it396// should cancel the previous one. It also relies on commitRoot scheduling a397// callback to render the next level, because that means we don't need a398// separate callback per expiration time.399function scheduleCallbackForRoot(400 root: FiberRoot,401 priorityLevel: ReactPriorityLevel,402 expirationTime: ExpirationTime,403) {404 const existingCallbackExpirationTime = root.callbackExpirationTime;405 if (existingCallbackExpirationTime < expirationTime) {406 // New callback has higher priority than the existing one.407 const existingCallbackNode = root.callbackNode;408 if (existingCallbackNode !== null) {409 cancelCallback(existingCallbackNode);410 }411 root.callbackExpirationTime = expirationTime;412 const options =413 expirationTime === Sync414 ? null415 : {timeout: expirationTimeToMs(expirationTime)};416 root.callbackNode = scheduleCallback(417 priorityLevel,418 runRootCallback.bind(419 null,420 root,421 renderRoot.bind(null, root, expirationTime),422 ),423 options,424 );425 if (426 enableUserTimingAPI &&427 expirationTime !== Sync &&428 workPhase !== RenderPhase &&429 workPhase !== CommitPhase430 ) {431 // Scheduled an async callback, and we're not already working. Add an432 // entry to the flamegraph that shows we're waiting for a callback433 // to fire.434 startRequestCallbackTimer();435 }436 }437 const timeoutHandle = root.timeoutHandle;438 if (timeoutHandle !== noTimeout) {439 // The root previous suspended and scheduled a timeout to commit a fallback440 // state. Now that we have additional work, cancel the timeout.441 root.timeoutHandle = noTimeout;442 // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above443 cancelTimeout(timeoutHandle);444 }445 // Add the current set of interactions to the pending set associated with446 // this root.447 schedulePendingInteraction(root, expirationTime);448}449function runRootCallback(root, callback, isSync) {450 const prevCallbackNode = root.callbackNode;451 let continuation = null;452 try {453 continuation = callback(isSync);454 if (continuation !== null) {455 return runRootCallback.bind(null, root, continuation);456 } else {457 return null;458 }459 } finally {460 // If the callback exits without returning a continuation, remove the461 // corresponding callback node from the root. Unless the callback node462 // has changed, which implies that it was already cancelled by a high463 // priority update.464 if (continuation === null && prevCallbackNode === root.callbackNode) {465 root.callbackNode = null;466 root.callbackExpirationTime = NoWork;467 }468 }469}470export function flushRoot(root: FiberRoot, expirationTime: ExpirationTime) {471 if (workPhase === RenderPhase || workPhase === CommitPhase) {472 invariant(473 false,474 'work.commit(): Cannot commit while already rendering. This likely ' +475 'means you attempted to commit from inside a lifecycle method.',476 );477 }478 scheduleCallback(479 ImmediatePriority,480 renderRoot.bind(null, root, expirationTime),481 );482 flushImmediateQueue();483}484export function flushInteractiveUpdates() {485 if (workPhase === RenderPhase || workPhase === CommitPhase) {486 // Can't synchronously flush interactive updates if React is already487 // working. This is currently a no-op.488 // TODO: Should we fire a warning? This happens if you synchronously invoke489 // an input event inside an effect, like with `element.click()`.490 return;491 }492 flushPendingDiscreteUpdates();493}494function resolveLocksOnRoot(root: FiberRoot, expirationTime: ExpirationTime) {495 const firstBatch = root.firstBatch;496 if (497 firstBatch !== null &&498 firstBatch._defer &&499 firstBatch._expirationTime >= expirationTime500 ) {501 root.finishedWork = root.current.alternate;502 root.pendingCommitExpirationTime = expirationTime;503 scheduleCallback(NormalPriority, () => {504 firstBatch._onComplete();505 return null;506 });507 return true;508 } else {509 return false;510 }511}512export function deferredUpdates<A>(fn: () => A): A {513 // TODO: Remove in favor of Scheduler.next514 return runWithPriority(NormalPriority, fn);515}516export function interactiveUpdates<A, B, C, R>(517 fn: (A, B, C) => R,518 a: A,519 b: B,520 c: C,521): R {522 if (workPhase === NotWorking) {523 // TODO: Remove this call. Instead of doing this automatically, the caller524 // should explicitly call flushInteractiveUpdates.525 flushPendingDiscreteUpdates();526 }527 return runWithPriority(UserBlockingPriority, fn.bind(null, a, b, c));528}529export function syncUpdates<A, B, C, R>(530 fn: (A, B, C) => R,531 a: A,532 b: B,533 c: C,534): R {535 return runWithPriority(ImmediatePriority, fn.bind(null, a, b, c));536}537function flushPendingDiscreteUpdates() {538 if (rootsWithPendingDiscreteUpdates !== null) {539 // For each root with pending discrete updates, schedule a callback to540 // immediately flush them.541 const roots = rootsWithPendingDiscreteUpdates;542 rootsWithPendingDiscreteUpdates = null;543 roots.forEach((expirationTime, root) => {544 scheduleCallback(545 ImmediatePriority,546 renderRoot.bind(null, root, expirationTime),547 );548 });549 // Now flush the immediate queue.550 flushImmediateQueue();551 }552}553export function batchedUpdates<A, R>(fn: A => R, a: A): R {554 if (workPhase !== NotWorking) {555 // We're already working, or inside a batch, so batchedUpdates is a no-op.556 return fn(a);557 }558 workPhase = BatchedPhase;559 try {560 return fn(a);561 } finally {562 workPhase = NotWorking;563 // Flush the immediate callbacks that were scheduled during this batch564 flushImmediateQueue();565 }566}567export function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {568 if (workPhase !== BatchedPhase && workPhase !== FlushSyncPhase) {569 // We're not inside batchedUpdates or flushSync, so unbatchedUpdates is570 // a no-op.571 return fn(a);572 }573 const prevWorkPhase = workPhase;574 workPhase = LegacyUnbatchedPhase;575 try {576 return fn(a);577 } finally {578 workPhase = prevWorkPhase;579 }580}581export function flushSync<A, R>(fn: A => R, a: A): R {582 if (workPhase === RenderPhase || workPhase === CommitPhase) {583 invariant(584 false,585 'flushSync was called from inside a lifecycle method. It cannot be ' +586 'called when React is already rendering.',587 );588 }589 const prevWorkPhase = workPhase;590 workPhase = FlushSyncPhase;591 try {592 return runWithPriority(ImmediatePriority, fn.bind(null, a));593 } finally {594 workPhase = prevWorkPhase;595 // Flush the immediate callbacks that were scheduled during this batch.596 // Note that this will happen even if batchedUpdates is higher up597 // the stack.598 flushImmediateQueue();599 }600}601export function flushControlled(fn: () => mixed): void {602 const prevWorkPhase = workPhase;603 workPhase = BatchedPhase;604 try {605 runWithPriority(ImmediatePriority, fn);606 } finally {607 workPhase = prevWorkPhase;608 if (workPhase === NotWorking) {609 // Flush the immediate callbacks that were scheduled during this batch610 flushImmediateQueue();611 }612 }613}614function prepareFreshStack(root, expirationTime) {615 root.pendingCommitExpirationTime = NoWork;616 if (workInProgress !== null) {617 let interruptedWork = workInProgress.return;618 while (interruptedWork !== null) {619 unwindInterruptedWork(interruptedWork);620 interruptedWork = interruptedWork.return;621 }622 }623 workInProgressRoot = root;624 workInProgress = createWorkInProgress(root.current, null, expirationTime);625 renderExpirationTime = expirationTime;626 workInProgressRootExitStatus = RootIncomplete;627 workInProgressRootAbsoluteTimeoutMs = -1;628 if (__DEV__) {629 ReactStrictModeWarnings.discardPendingWarnings();630 }631}632function renderRoot(633 root: FiberRoot,634 expirationTime: ExpirationTime,635 isSync: boolean,636): SchedulerCallback | null {637 invariant(638 workPhase !== RenderPhase && workPhase !== CommitPhase,639 'Should not already be working.',640 );641 if (enableUserTimingAPI && expirationTime !== Sync) {642 const didExpire = isSync;643 const timeoutMs = expirationTimeToMs(expirationTime);644 stopRequestCallbackTimer(didExpire, timeoutMs);645 }646 if (root.firstPendingTime < expirationTime) {647 // If there's no work left at this expiration time, exit immediately. This648 // happens when multiple callbacks are scheduled for a single root, but an649 // earlier callback flushes the work of a later one.650 return null;651 }652 if (root.pendingCommitExpirationTime === expirationTime) {653 // There's already a pending commit at this expiration time.654 root.pendingCommitExpirationTime = NoWork;655 return commitRoot.bind(null, root, expirationTime);656 }657 flushPassiveEffects();658 // If the root or expiration time have changed, throw out the existing stack659 // and prepare a fresh one. Otherwise we'll continue where we left off.660 if (root !== workInProgressRoot || expirationTime !== renderExpirationTime) {661 prepareFreshStack(root, expirationTime);662 startWorkOnPendingInteraction(root, expirationTime);663 }664 // If we have a work-in-progress fiber, it means there's still work to do665 // in this root.666 if (workInProgress !== null) {667 const prevWorkPhase = workPhase;668 workPhase = RenderPhase;669 let prevDispatcher = ReactCurrentDispatcher.current;670 if (prevDispatcher === null) {671 // The React isomorphic package does not include a default dispatcher.672 // Instead the first renderer will lazily attach one, in order to give673 // nicer error messages.674 prevDispatcher = ContextOnlyDispatcher;675 }676 ReactCurrentDispatcher.current = ContextOnlyDispatcher;677 let prevInteractions: Set<Interaction> | null = null;678 if (enableSchedulerTracing) {679 prevInteractions = __interactionsRef.current;680 __interactionsRef.current = root.memoizedInteractions;681 }682 startWorkLoopTimer(workInProgress);683 // TODO: Fork renderRoot into renderRootSync and renderRootAsync684 if (isSync) {685 if (expirationTime !== Sync) {686 // An async update expired. There may be other expired updates on687 // this root. We should render all the expired work in a688 // single batch.689 const currentTime = requestCurrentTime();690 if (currentTime < expirationTime) {691 // Restart at the current time.692 workPhase = prevWorkPhase;693 resetContextDependencies();694 ReactCurrentDispatcher.current = prevDispatcher;695 if (enableSchedulerTracing) {696 __interactionsRef.current = ((prevInteractions: any): Set<697 Interaction,698 >);699 }700 return renderRoot.bind(null, root, currentTime);701 }702 }703 } else {704 // Since we know we're in a React event, we can clear the current705 // event time. The next update will compute a new event time.706 currentEventTime = NoWork;707 }708 do {709 try {710 if (isSync) {711 workLoopSync();712 } else {713 workLoop();714 }715 break;716 } catch (thrownValue) {717 // Reset module-level state that was set during the render phase.718 resetContextDependencies();719 resetHooks();720 const sourceFiber = workInProgress;721 if (sourceFiber === null || sourceFiber.return === null) {722 // Expected to be working on a non-root fiber. This is a fatal error723 // because there's no ancestor that can handle it; the root is724 // supposed to capture all errors that weren't caught by an error725 // boundary.726 prepareFreshStack(root, expirationTime);727 workPhase = prevWorkPhase;728 throw thrownValue;729 }730 if (enableProfilerTimer && sourceFiber.mode & ProfileMode) {731 // Record the time spent rendering before an error was thrown. This732 // avoids inaccurate Profiler durations in the case of a733 // suspended render.734 stopProfilerTimerIfRunningAndRecordDelta(sourceFiber, true);735 }736 const returnFiber = sourceFiber.return;737 throwException(738 root,739 returnFiber,740 sourceFiber,741 thrownValue,742 renderExpirationTime,743 );744 workInProgress = completeUnitOfWork(sourceFiber);745 }746 } while (true);747 workPhase = prevWorkPhase;748 resetContextDependencies();749 ReactCurrentDispatcher.current = prevDispatcher;750 if (enableSchedulerTracing) {751 __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);752 }753 if (workInProgress !== null) {754 // There's still work left over. Return a continuation.755 stopInterruptedWorkLoopTimer();756 if (expirationTime !== Sync) {757 startRequestCallbackTimer();758 }759 return renderRoot.bind(null, root, expirationTime);760 }761 }762 // We now have a consistent tree. The next step is either to commit it, or, if763 // something suspended, wait to commit it after a timeout.764 stopFinishedWorkLoopTimer();765 const isLocked = resolveLocksOnRoot(root, expirationTime);766 if (isLocked) {767 // This root has a lock that prevents it from committing. Exit. If we begin768 // work on the root again, without any intervening updates, it will finish769 // without doing additional work.770 return null;771 }772 // Set this to null to indicate there's no in-progress render.773 workInProgressRoot = null;774 switch (workInProgressRootExitStatus) {775 case RootIncomplete: {776 invariant(false, 'Should have a work-in-progress.');777 }778 // Flow knows about invariant, so it compains if I add a break statement,779 // but eslint doesn't know about invariant, so it complains if I do.780 // eslint-disable-next-line no-fallthrough781 case RootErrored: {782 // An error was thrown. First check if there is lower priority work783 // scheduled on this root.784 const lastPendingTime = root.lastPendingTime;785 if (root.lastPendingTime < expirationTime) {786 // There's lower priority work. Before raising the error, try rendering787 // at the lower priority to see if it fixes it. Use a continuation to788 // maintain the existing priority and position in the queue.789 return renderRoot.bind(null, root, lastPendingTime);790 }791 if (!isSync) {792 // If we're rendering asynchronously, it's possible the error was793 // caused by tearing due to a mutation during an event. Try rendering794 // one more time without yiedling to events.795 prepareFreshStack(root, expirationTime);796 scheduleCallback(797 ImmediatePriority,798 renderRoot.bind(null, root, expirationTime),799 );800 return null;801 }802 // If we're already rendering synchronously, commit the root in its803 // errored state.804 return commitRoot.bind(null, root, expirationTime);805 }806 case RootSuspended: {807 const lastPendingTime = root.lastPendingTime;808 if (root.lastPendingTime < expirationTime) {809 // There's lower priority work. It might be unsuspended. Try rendering810 // at that level.811 return renderRoot.bind(null, root, lastPendingTime);812 }813 if (!isSync) {814 const msUntilTimeout = computeMsUntilTimeout(815 root,816 workInProgressRootAbsoluteTimeoutMs,817 );818 if (msUntilTimeout > 0) {819 // The render is suspended, it hasn't timed out, and there's no lower820 // priority work to do. Instead of committing the fallback821 // immediately, wait for more data to arrive.822 root.timeoutHandle = scheduleTimeout(823 commitRoot.bind(null, root, expirationTime),824 msUntilTimeout,825 );826 return null;827 }828 }829 // The work expired. Commit immediately.830 return commitRoot.bind(null, root, expirationTime);831 }832 case RootCompleted: {833 // The work completed. Ready to commit.834 return commitRoot.bind(null, root, expirationTime);835 }836 default: {837 invariant(false, 'Unknown root exit status.');838 }839 }840}841export function renderDidSuspend(842 root: FiberRoot,843 absoluteTimeoutMs: number,844 // TODO: Don't need this argument anymore845 suspendedTime: ExpirationTime,846) {847 if (848 absoluteTimeoutMs >= 0 &&849 workInProgressRootAbsoluteTimeoutMs < absoluteTimeoutMs850 ) {851 workInProgressRootAbsoluteTimeoutMs = absoluteTimeoutMs;852 if (workInProgressRootExitStatus === RootIncomplete) {853 workInProgressRootExitStatus = RootSuspended;854 }855 }856}857export function renderDidError() {858 if (859 workInProgressRootExitStatus === RootIncomplete ||860 workInProgressRootExitStatus === RootSuspended861 ) {862 workInProgressRootExitStatus = RootErrored;863 }864}865function workLoopSync() {866 // Already timed out, so perform work without checking if we need to yield.867 while (workInProgress !== null) {868 workInProgress = performUnitOfWork(workInProgress);869 }870}871function workLoop() {872 // Perform work until Scheduler asks us to yield873 while (workInProgress !== null && !shouldYield()) {874 workInProgress = performUnitOfWork(workInProgress);875 }876}877function performUnitOfWork(unitOfWork: Fiber): Fiber | null {878 // The current, flushed, state of this fiber is the alternate. Ideally879 // nothing should rely on this, but relying on it here means that we don't880 // need an additional field on the work in progress.881 const current = unitOfWork.alternate;882 startWorkTimer(unitOfWork);883 setCurrentDebugFiberInDEV(unitOfWork);884 let next;885 if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoContext) {886 startProfilerTimer(unitOfWork);887 next = beginWork(current, unitOfWork, renderExpirationTime);888 stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);889 } else {890 next = beginWork(current, unitOfWork, renderExpirationTime);891 }892 resetCurrentDebugFiberInDEV();893 unitOfWork.memoizedProps = unitOfWork.pendingProps;894 if (next === null) {895 // If this doesn't spawn new work, complete the current work.896 next = completeUnitOfWork(unitOfWork);897 }898 ReactCurrentOwner.current = null;899 return next;900}901function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {902 // Attempt to complete the current unit of work, then move to the next903 // sibling. If there are no more siblings, return to the parent fiber.904 workInProgress = unitOfWork;905 do {906 // The current, flushed, state of this fiber is the alternate. Ideally907 // nothing should rely on this, but relying on it here means that we don't908 // need an additional field on the work in progress.909 const current = workInProgress.alternate;910 const returnFiber = workInProgress.return;911 // Check if the work completed or if something threw.912 if ((workInProgress.effectTag & Incomplete) === NoEffect) {913 setCurrentDebugFiberInDEV(workInProgress);914 let next;915 if (916 !enableProfilerTimer ||917 (workInProgress.mode & ProfileMode) === NoContext918 ) {919 next = completeWork(current, workInProgress, renderExpirationTime);920 } else {921 startProfilerTimer(workInProgress);922 next = completeWork(current, workInProgress, renderExpirationTime);923 // Update render duration assuming we didn't error.924 stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);925 }926 stopWorkTimer(workInProgress);927 resetCurrentDebugFiberInDEV();928 resetChildExpirationTime(workInProgress);929 if (next !== null) {930 // Completing this fiber spawned new work. Work on that next.931 return next;932 }933 if (934 returnFiber !== null &&935 // Do not append effects to parents if a sibling failed to complete936 (returnFiber.effectTag & Incomplete) === NoEffect937 ) {938 // Append all the effects of the subtree and this fiber onto the effect939 // list of the parent. The completion order of the children affects the940 // side-effect order.941 if (returnFiber.firstEffect === null) {942 returnFiber.firstEffect = workInProgress.firstEffect;943 }944 if (workInProgress.lastEffect !== null) {945 if (returnFiber.lastEffect !== null) {946 returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;947 }948 returnFiber.lastEffect = workInProgress.lastEffect;949 }950 // If this fiber had side-effects, we append it AFTER the children's951 // side-effects. We can perform certain side-effects earlier if needed,952 // by doing multiple passes over the effect list. We don't want to953 // schedule our own side-effect on our own list because if end up954 // reusing children we'll schedule this effect onto itself since we're955 // at the end.956 const effectTag = workInProgress.effectTag;957 // Skip both NoWork and PerformedWork tags when creating the effect958 // list. PerformedWork effect is read by React DevTools but shouldn't be959 // committed.960 if (effectTag > PerformedWork) {961 if (returnFiber.lastEffect !== null) {962 returnFiber.lastEffect.nextEffect = workInProgress;963 } else {964 returnFiber.firstEffect = workInProgress;965 }966 returnFiber.lastEffect = workInProgress;967 }968 }969 } else {970 // This fiber did not complete because something threw. Pop values off971 // the stack without entering the complete phase. If this is a boundary,972 // capture values if possible.973 const next = unwindWork(workInProgress, renderExpirationTime);974 // Because this fiber did not complete, don't reset its expiration time.975 if (976 enableProfilerTimer &&977 (workInProgress.mode & ProfileMode) !== NoContext978 ) {979 // Record the render duration for the fiber that errored.980 stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);981 // Include the time spent working on failed children before continuing.982 let actualDuration = workInProgress.actualDuration;983 let child = workInProgress.child;984 while (child !== null) {985 actualDuration += child.actualDuration;986 child = child.sibling;987 }988 workInProgress.actualDuration = actualDuration;989 }990 if (next !== null) {991 // If completing this work spawned new work, do that next. We'll come992 // back here again.993 // Since we're restarting, remove anything that is not a host effect994 // from the effect tag.995 // TODO: The name stopFailedWorkTimer is misleading because Suspense996 // also captures and restarts.997 stopFailedWorkTimer(workInProgress);998 next.effectTag &= HostEffectMask;999 return next;1000 }1001 stopWorkTimer(workInProgress);1002 if (returnFiber !== null) {1003 // Mark the parent fiber as incomplete and clear its effect list.1004 returnFiber.firstEffect = returnFiber.lastEffect = null;1005 returnFiber.effectTag |= Incomplete;1006 }1007 }1008 const siblingFiber = workInProgress.sibling;1009 if (siblingFiber !== null) {1010 // If there is more work to do in this returnFiber, do that next.1011 return siblingFiber;1012 }1013 // Otherwise, return to the parent1014 workInProgress = returnFiber;1015 } while (workInProgress !== null);1016 // We've reached the root.1017 if (workInProgressRootExitStatus === RootIncomplete) {1018 workInProgressRootExitStatus = RootCompleted;1019 }1020 return null;1021}1022function resetChildExpirationTime(completedWork: Fiber) {1023 if (1024 renderExpirationTime !== Never &&1025 completedWork.childExpirationTime === Never1026 ) {1027 // The children of this component are hidden. Don't bubble their1028 // expiration times.1029 return;1030 }1031 let newChildExpirationTime = NoWork;1032 // Bubble up the earliest expiration time.1033 if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoContext) {1034 // In profiling mode, resetChildExpirationTime is also used to reset1035 // profiler durations.1036 let actualDuration = completedWork.actualDuration;1037 let treeBaseDuration = completedWork.selfBaseDuration;1038 // When a fiber is cloned, its actualDuration is reset to 0. This value will1039 // only be updated if work is done on the fiber (i.e. it doesn't bailout).1040 // When work is done, it should bubble to the parent's actualDuration. If1041 // the fiber has not been cloned though, (meaning no work was done), then1042 // this value will reflect the amount of time spent working on a previous1043 // render. In that case it should not bubble. We determine whether it was1044 // cloned by comparing the child pointer.1045 const shouldBubbleActualDurations =1046 completedWork.alternate === null ||1047 completedWork.child !== completedWork.alternate.child;1048 let child = completedWork.child;1049 while (child !== null) {1050 const childUpdateExpirationTime = child.expirationTime;1051 const childChildExpirationTime = child.childExpirationTime;1052 if (childUpdateExpirationTime > newChildExpirationTime) {1053 newChildExpirationTime = childUpdateExpirationTime;1054 }1055 if (childChildExpirationTime > newChildExpirationTime) {1056 newChildExpirationTime = childChildExpirationTime;1057 }1058 if (shouldBubbleActualDurations) {1059 actualDuration += child.actualDuration;1060 }1061 treeBaseDuration += child.treeBaseDuration;1062 child = child.sibling;1063 }1064 completedWork.actualDuration = actualDuration;1065 completedWork.treeBaseDuration = treeBaseDuration;1066 } else {1067 let child = completedWork.child;1068 while (child !== null) {1069 const childUpdateExpirationTime = child.expirationTime;1070 const childChildExpirationTime = child.childExpirationTime;1071 if (childUpdateExpirationTime > newChildExpirationTime) {1072 newChildExpirationTime = childUpdateExpirationTime;1073 }1074 if (childChildExpirationTime > newChildExpirationTime) {1075 newChildExpirationTime = childChildExpirationTime;1076 }1077 child = child.sibling;1078 }1079 }1080 completedWork.childExpirationTime = newChildExpirationTime;1081}1082function commitRoot(root, expirationTime) {1083 runWithPriority(1084 ImmediatePriority,1085 commitRootImpl.bind(null, root, expirationTime),1086 );1087 // If there are passive effects, schedule a callback to flush them. This goes1088 // outside commitRootImpl so that it inherits the priority of the render.1089 if (rootWithPendingPassiveEffects !== null) {1090 const priorityLevel = getCurrentPriorityLevel();1091 scheduleCallback(priorityLevel, () => {1092 flushPassiveEffects();1093 return null;1094 });1095 }1096 return null;1097}1098function commitRootImpl(root, expirationTime) {1099 flushPassiveEffects();1100 flushRenderPhaseStrictModeWarningsInDEV();1101 invariant(1102 workPhase !== RenderPhase && workPhase !== CommitPhase,1103 'Should not already be working.',1104 );1105 const finishedWork = root.current.alternate;1106 invariant(finishedWork !== null, 'Should have a work-in-progress root.');1107 // commitRoot never returns a continuation; it always finishes synchronously.1108 // So we can clear these now to allow a new callback to be scheduled.1109 root.callbackNode = null;1110 root.callbackExpirationTime = NoWork;1111 startCommitTimer();1112 // Update the first and last pending times on this root. The new first1113 // pending time is whatever is left on the root fiber.1114 const updateExpirationTimeBeforeCommit = finishedWork.expirationTime;1115 const childExpirationTimeBeforeCommit = finishedWork.childExpirationTime;1116 const firstPendingTimeBeforeCommit =1117 childExpirationTimeBeforeCommit > updateExpirationTimeBeforeCommit1118 ? childExpirationTimeBeforeCommit1119 : updateExpirationTimeBeforeCommit;1120 root.firstPendingTime = firstPendingTimeBeforeCommit;1121 if (firstPendingTimeBeforeCommit < root.lastPendingTime) {1122 // This usually means we've finished all the work, but it can also happen1123 // when something gets downprioritized during render, like a hidden tree.1124 root.lastPendingTime = firstPendingTimeBeforeCommit;1125 }1126 if (root === workInProgressRoot) {1127 // We can reset these now that they are finished.1128 workInProgressRoot = null;1129 workInProgress = null;1130 renderExpirationTime = NoWork;1131 } else {1132 // This indicates that the last root we worked on is not the same one that1133 // we're committing now. This most commonly happens when a suspended root1134 // times out.1135 }1136 // Get the list of effects.1137 let firstEffect;1138 if (finishedWork.effectTag > PerformedWork) {1139 // A fiber's effect list consists only of its children, not itself. So if1140 // the root has an effect, we need to add it to the end of the list. The1141 // resulting list is the set that would belong to the root's parent, if it1142 // had one; that is, all the effects in the tree including the root.1143 if (finishedWork.lastEffect !== null) {1144 finishedWork.lastEffect.nextEffect = finishedWork;1145 firstEffect = finishedWork.firstEffect;1146 } else {1147 firstEffect = finishedWork;1148 }1149 } else {1150 // There is no effect on the root.1151 firstEffect = finishedWork.firstEffect;1152 }1153 if (firstEffect !== null) {1154 const prevWorkPhase = workPhase;1155 workPhase = CommitPhase;1156 let prevInteractions: Set<Interaction> | null = null;1157 if (enableSchedulerTracing) {1158 prevInteractions = __interactionsRef.current;1159 __interactionsRef.current = root.memoizedInteractions;1160 }1161 // Reset this to null before calling lifecycles1162 ReactCurrentOwner.current = null;1163 // The commit phase is broken into several sub-phases. We do a separate pass1164 // of the effect list for each phase: all mutation effects come before all1165 // layout effects, and so on.1166 // The first phase a "before mutation" phase. We use this phase to read the1167 // state of the host tree right before we mutate it. This is where1168 // getSnapshotBeforeUpdate is called.1169 startCommitSnapshotEffectsTimer();1170 prepareForCommit(root.containerInfo);1171 nextEffect = firstEffect;1172 do {1173 if (__DEV__) {1174 invokeGuardedCallback(null, commitBeforeMutationEffects, null);1175 if (hasCaughtError()) {1176 invariant(nextEffect !== null, 'Should be working on an effect.');1177 const error = clearCaughtError();1178 captureCommitPhaseError(nextEffect, error);1179 nextEffect = nextEffect.nextEffect;1180 }1181 } else {1182 try {1183 commitBeforeMutationEffects();1184 } catch (error) {1185 invariant(nextEffect !== null, 'Should be working on an effect.');1186 captureCommitPhaseError(nextEffect, error);1187 nextEffect = nextEffect.nextEffect;1188 }1189 }1190 } while (nextEffect !== null);1191 stopCommitSnapshotEffectsTimer();1192 if (enableProfilerTimer) {1193 // Mark the current commit time to be shared by all Profilers in this1194 // batch. This enables them to be grouped later.1195 recordCommitTime();1196 }1197 // The next phase is the mutation phase, where we mutate the host tree.1198 startCommitHostEffectsTimer();1199 nextEffect = firstEffect;1200 do {1201 if (__DEV__) {1202 invokeGuardedCallback(null, commitMutationEffects, null);1203 if (hasCaughtError()) {1204 invariant(nextEffect !== null, 'Should be working on an effect.');1205 const error = clearCaughtError();1206 captureCommitPhaseError(nextEffect, error);1207 nextEffect = nextEffect.nextEffect;1208 }1209 } else {1210 try {1211 commitMutationEffects();1212 } catch (error) {1213 invariant(nextEffect !== null, 'Should be working on an effect.');1214 captureCommitPhaseError(nextEffect, error);1215 nextEffect = nextEffect.nextEffect;1216 }1217 }1218 } while (nextEffect !== null);1219 stopCommitHostEffectsTimer();1220 resetAfterCommit(root.containerInfo);1221 // The work-in-progress tree is now the current tree. This must come after1222 // the mutation phase, so that the previous tree is still current during1223 // componentWillUnmount, but before the layout phase, so that the finished1224 // work is current during componentDidMount/Update.1225 root.current = finishedWork;1226 // The next phase is the layout phase, where we call effects that read1227 // the host tree after it's been mutated. The idiomatic use case for this is1228 // layout, but class component lifecycles also fire here for legacy reasons.1229 startCommitLifeCyclesTimer();1230 nextEffect = firstEffect;1231 do {1232 if (__DEV__) {1233 invokeGuardedCallback(1234 null,1235 commitLayoutEffects,1236 null,1237 root,1238 expirationTime,1239 );1240 if (hasCaughtError()) {1241 invariant(nextEffect !== null, 'Should be working on an effect.');1242 const error = clearCaughtError();1243 captureCommitPhaseError(nextEffect, error);1244 nextEffect = nextEffect.nextEffect;1245 }1246 } else {1247 try {1248 commitLayoutEffects(root, expirationTime);1249 } catch (error) {1250 invariant(nextEffect !== null, 'Should be working on an effect.');1251 captureCommitPhaseError(nextEffect, error);1252 nextEffect = nextEffect.nextEffect;1253 }1254 }1255 } while (nextEffect !== null);1256 stopCommitLifeCyclesTimer();1257 nextEffect = null;1258 if (enableSchedulerTracing) {1259 __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1260 }1261 workPhase = prevWorkPhase;1262 } else {1263 // No effects.1264 root.current = finishedWork;1265 // Measure these anyway so the flamegraph explicitly shows that there were1266 // no effects.1267 // TODO: Maybe there's a better way to report this.1268 startCommitSnapshotEffectsTimer();1269 stopCommitSnapshotEffectsTimer();1270 if (enableProfilerTimer) {1271 recordCommitTime();1272 }1273 startCommitHostEffectsTimer();1274 stopCommitHostEffectsTimer();1275 startCommitLifeCyclesTimer();1276 stopCommitLifeCyclesTimer();1277 }1278 stopCommitTimer();1279 if (rootDoesHavePassiveEffects) {1280 // This commit has passive effects. Stash a reference to them. But don't1281 // schedule a callback until after flushing layout work.1282 rootDoesHavePassiveEffects = false;1283 rootWithPendingPassiveEffects = root;1284 pendingPassiveEffectsExpirationTime = expirationTime;1285 } else {1286 if (enableSchedulerTracing) {1287 // If there are no passive effects, then we can complete the pending1288 // interactions. Otherwise, we'll wait until after the passive effects1289 // are flushed.1290 finishPendingInteractions(root, expirationTime);1291 }1292 }1293 // Check if there's remaining work on this root1294 const remainingExpirationTime = root.firstPendingTime;1295 if (remainingExpirationTime !== NoWork) {1296 const currentTime = requestCurrentTime();1297 const priorityLevel = inferPriorityFromExpirationTime(1298 currentTime,1299 remainingExpirationTime,1300 );1301 scheduleCallbackForRoot(root, priorityLevel, remainingExpirationTime);1302 } else {1303 // If there's no remaining work, we can clear the set of already failed1304 // error boundaries.1305 legacyErrorBoundariesThatAlreadyFailed = null;1306 }1307 onCommitRoot(finishedWork.stateNode);1308 if (remainingExpirationTime === Sync) {1309 // Count the number of times the root synchronously re-renders without1310 // finishing. If there are too many, it indicates an infinite update loop.1311 if (root === rootWithNestedUpdates) {1312 nestedUpdateCount++;1313 } else {1314 nestedUpdateCount = 0;1315 rootWithNestedUpdates = root;1316 }1317 } else {1318 nestedUpdateCount = 0;1319 }1320 if (hasUncaughtError) {1321 hasUncaughtError = false;1322 const error = firstUncaughtError;1323 firstUncaughtError = null;1324 throw error;1325 }1326 if (workPhase === LegacyUnbatchedPhase) {1327 // This is a legacy edge case. We just committed the initial mount of1328 // a ReactDOM.render-ed root inside of batchedUpdates. The commit fired1329 // synchronously, but layout updates should be deferred until the end1330 // of the batch.1331 return null;1332 }1333 // If layout work was scheduled, flush it now.1334 flushImmediateQueue();1335 return null;1336}1337function commitBeforeMutationEffects() {1338 while (nextEffect !== null) {1339 if ((nextEffect.effectTag & Snapshot) !== NoEffect) {1340 setCurrentDebugFiberInDEV(nextEffect);1341 recordEffect();1342 const current = nextEffect.alternate;1343 commitBeforeMutationEffectOnFiber(current, nextEffect);1344 resetCurrentDebugFiberInDEV();1345 }1346 nextEffect = nextEffect.nextEffect;1347 }1348}1349function commitMutationEffects() {1350 // TODO: Should probably move the bulk of this function to commitWork.1351 while (nextEffect !== null) {1352 setCurrentDebugFiberInDEV(nextEffect);1353 const effectTag = nextEffect.effectTag;1354 if (effectTag & ContentReset) {1355 commitResetTextContent(nextEffect);1356 }1357 if (effectTag & Ref) {1358 const current = nextEffect.alternate;1359 if (current !== null) {1360 commitDetachRef(current);1361 }1362 }1363 // The following switch statement is only concerned about placement,1364 // updates, and deletions. To avoid needing to add a case for every possible1365 // bitmap value, we remove the secondary effects from the effect tag and1366 // switch on that value.1367 let primaryEffectTag = effectTag & (Placement | Update | Deletion);1368 switch (primaryEffectTag) {1369 case Placement: {1370 commitPlacement(nextEffect);1371 // Clear the "placement" from effect tag so that we know that this is1372 // inserted, before any life-cycles like componentDidMount gets called.1373 // TODO: findDOMNode doesn't rely on this any more but isMounted does1374 // and isMounted is deprecated anyway so we should be able to kill this.1375 nextEffect.effectTag &= ~Placement;1376 break;1377 }1378 case PlacementAndUpdate: {1379 // Placement1380 commitPlacement(nextEffect);1381 // Clear the "placement" from effect tag so that we know that this is1382 // inserted, before any life-cycles like componentDidMount gets called.1383 nextEffect.effectTag &= ~Placement;1384 // Update1385 const current = nextEffect.alternate;1386 commitWork(current, nextEffect);1387 break;1388 }1389 case Update: {1390 const current = nextEffect.alternate;1391 commitWork(current, nextEffect);1392 break;1393 }1394 case Deletion: {1395 commitDeletion(nextEffect);1396 break;1397 }1398 }1399 // TODO: Only record a mutation effect if primaryEffectTag is non-zero.1400 recordEffect();1401 resetCurrentDebugFiberInDEV();1402 nextEffect = nextEffect.nextEffect;1403 }1404}1405function commitLayoutEffects(1406 root: FiberRoot,1407 committedExpirationTime: ExpirationTime,1408) {1409 // TODO: Should probably move the bulk of this function to commitWork.1410 while (nextEffect !== null) {1411 setCurrentDebugFiberInDEV(nextEffect);1412 const effectTag = nextEffect.effectTag;1413 if (effectTag & (Update | Callback)) {1414 recordEffect();1415 const current = nextEffect.alternate;1416 commitLayoutEffectOnFiber(1417 root,1418 current,1419 nextEffect,1420 committedExpirationTime,1421 );1422 }1423 if (effectTag & Ref) {1424 recordEffect();1425 commitAttachRef(nextEffect);1426 }1427 if (effectTag & Passive) {1428 rootDoesHavePassiveEffects = true;1429 }1430 resetCurrentDebugFiberInDEV();1431 nextEffect = nextEffect.nextEffect;1432 }1433}1434export function flushPassiveEffects() {1435 if (rootWithPendingPassiveEffects === null) {1436 return false;1437 }1438 const root = rootWithPendingPassiveEffects;1439 const expirationTime = pendingPassiveEffectsExpirationTime;1440 rootWithPendingPassiveEffects = null;1441 pendingPassiveEffectsExpirationTime = NoWork;1442 let prevInteractions: Set<Interaction> | null = null;1443 if (enableSchedulerTracing) {1444 prevInteractions = __interactionsRef.current;1445 __interactionsRef.current = root.memoizedInteractions;1446 }1447 invariant(1448 workPhase !== RenderPhase && workPhase !== CommitPhase,1449 'Cannot flush passive effects while already rendering.',1450 );1451 const prevWorkPhase = workPhase;1452 workPhase = CommitPhase;1453 // Note: This currently assumes there are no passive effects on the root1454 // fiber, because the root is not part of its own effect list. This could1455 // change in the future.1456 let effect = root.current.firstEffect;1457 while (effect !== null) {1458 if (__DEV__) {1459 setCurrentDebugFiberInDEV(effect);1460 invokeGuardedCallback(null, commitPassiveHookEffects, null, effect);1461 if (hasCaughtError()) {1462 invariant(effect !== null, 'Should be working on an effect.');1463 const error = clearCaughtError();1464 captureCommitPhaseError(effect, error);1465 }1466 resetCurrentDebugFiberInDEV();1467 } else {1468 try {1469 commitPassiveHookEffects(effect);1470 } catch (error) {1471 invariant(effect !== null, 'Should be working on an effect.');1472 captureCommitPhaseError(effect, error);1473 }1474 }1475 effect = effect.nextEffect;1476 }1477 if (enableSchedulerTracing) {1478 __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1479 finishPendingInteractions(root, expirationTime);1480 }1481 workPhase = prevWorkPhase;1482 flushImmediateQueue();1483 // If additional passive effects were scheduled, increment a counter. If this1484 // exceeds the limit, we'll fire a warning.1485 nestedPassiveUpdateCount =1486 rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;1487 return true;1488}1489export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {1490 return (1491 legacyErrorBoundariesThatAlreadyFailed !== null &&1492 legacyErrorBoundariesThatAlreadyFailed.has(instance)1493 );1494}1495export function markLegacyErrorBoundaryAsFailed(instance: mixed) {1496 if (legacyErrorBoundariesThatAlreadyFailed === null) {1497 legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);1498 } else {1499 legacyErrorBoundariesThatAlreadyFailed.add(instance);1500 }1501}1502function prepareToThrowUncaughtError(error: mixed) {1503 if (!hasUncaughtError) {1504 hasUncaughtError = true;1505 firstUncaughtError = error;1506 }1507}1508export const onUncaughtError = prepareToThrowUncaughtError;1509function captureCommitPhaseErrorOnRoot(1510 rootFiber: Fiber,1511 sourceFiber: Fiber,1512 error: mixed,1513) {1514 const errorInfo = createCapturedValue(error, sourceFiber);1515 const update = createRootErrorUpdate(rootFiber, errorInfo, Sync);1516 enqueueUpdate(rootFiber, update);1517 const root = markUpdateTimeFromFiberToRoot(rootFiber, Sync);1518 if (root !== null) {1519 scheduleCallbackForRoot(root, ImmediatePriority, Sync);1520 }1521}1522export function captureCommitPhaseError(sourceFiber: Fiber, error: mixed) {1523 if (sourceFiber.tag === HostRoot) {1524 // Error was thrown at the root. There is no parent, so the root1525 // itself should capture it.1526 captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);1527 return;1528 }1529 let fiber = sourceFiber.return;1530 while (fiber !== null) {1531 if (fiber.tag === HostRoot) {1532 captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);1533 return;1534 } else if (fiber.tag === ClassComponent) {1535 const ctor = fiber.type;1536 const instance = fiber.stateNode;1537 if (1538 typeof ctor.getDerivedStateFromError === 'function' ||1539 (typeof instance.componentDidCatch === 'function' &&1540 !isAlreadyFailedLegacyErrorBoundary(instance))1541 ) {1542 const errorInfo = createCapturedValue(error, sourceFiber);1543 const update = createClassErrorUpdate(1544 fiber,1545 errorInfo,1546 // TODO: This is always sync1547 Sync,1548 );1549 enqueueUpdate(fiber, update);1550 const root = markUpdateTimeFromFiberToRoot(fiber, Sync);1551 if (root !== null) {1552 scheduleCallbackForRoot(root, ImmediatePriority, Sync);1553 }1554 return;1555 }1556 }1557 fiber = fiber.return;1558 }1559}1560export function pingSuspendedRoot(1561 root: FiberRoot,1562 thenable: Thenable,1563 suspendedTime: ExpirationTime,1564) {1565 const pingCache = root.pingCache;1566 if (pingCache !== null) {1567 // The thenable resolved, so we no longer need to memoize, because it will1568 // never be thrown again.1569 pingCache.delete(thenable);1570 }1571 if (workInProgressRoot === root && renderExpirationTime === suspendedTime) {1572 // Received a ping at the same priority level at which we're currently1573 // rendering. Restart from the root. Don't need to schedule a ping because1574 // we're already working on this tree.1575 prepareFreshStack(root, renderExpirationTime);1576 return;1577 }1578 const lastPendingTime = root.lastPendingTime;1579 if (lastPendingTime < suspendedTime) {1580 // The root is no longer suspended at this time.1581 return;1582 }1583 const pingTime = root.pingTime;1584 if (pingTime !== NoWork && pingTime < suspendedTime) {1585 // There's already a lower priority ping scheduled.1586 return;1587 }1588 // Mark the time at which this ping was scheduled.1589 root.pingTime = suspendedTime;1590 const currentTime = requestCurrentTime();1591 const priorityLevel = inferPriorityFromExpirationTime(1592 currentTime,1593 suspendedTime,1594 );1595 scheduleCallbackForRoot(root, priorityLevel, suspendedTime);1596}1597export function retryTimedOutBoundary(boundaryFiber: Fiber) {1598 // The boundary fiber (a Suspense component) previously timed out and was1599 // rendered in its fallback state. One of the promises that suspended it has1600 // resolved, which means at least part of the tree was likely unblocked. Try1601 // rendering again, at a new expiration time.1602 const currentTime = requestCurrentTime();1603 const retryTime = computeExpirationForFiber(currentTime, boundaryFiber);1604 // TODO: Special case idle priority?1605 const priorityLevel = inferPriorityFromExpirationTime(currentTime, retryTime);1606 const root = markUpdateTimeFromFiberToRoot(boundaryFiber, retryTime);1607 if (root !== null) {1608 scheduleCallbackForRoot(root, priorityLevel, retryTime);1609 }1610}1611export function resolveRetryThenable(boundaryFiber: Fiber, thenable: Thenable) {1612 let retryCache: WeakSet<Thenable> | Set<Thenable> | null;1613 if (enableSuspenseServerRenderer) {1614 switch (boundaryFiber.tag) {1615 case SuspenseComponent:1616 retryCache = boundaryFiber.stateNode;1617 break;1618 case DehydratedSuspenseComponent:1619 retryCache = boundaryFiber.memoizedState;1620 break;1621 default:1622 invariant(1623 false,1624 'Pinged unknown suspense boundary type. ' +1625 'This is probably a bug in React.',1626 );1627 }1628 } else {1629 retryCache = boundaryFiber.stateNode;1630 }1631 if (retryCache !== null) {1632 // The thenable resolved, so we no longer need to memoize, because it will1633 // never be thrown again.1634 retryCache.delete(thenable);1635 }1636 retryTimedOutBoundary(boundaryFiber);1637}1638export function inferStartTimeFromExpirationTime(1639 root: FiberRoot,1640 expirationTime: ExpirationTime,1641) {1642 // We don't know exactly when the update was scheduled, but we can infer an1643 // approximate start time from the expiration time.1644 const earliestExpirationTimeMs = expirationTimeToMs(root.firstPendingTime);1645 // TODO: Track this on the root instead. It's more accurate, doesn't rely on1646 // assumptions about priority, and isn't coupled to Scheduler details.1647 return earliestExpirationTimeMs - LOW_PRIORITY_EXPIRATION;1648}1649function computeMsUntilTimeout(root, absoluteTimeoutMs) {1650 if (disableYielding) {1651 // Timeout immediately when yielding is disabled.1652 return 0;1653 }1654 // Find the earliest uncommitted expiration time in the tree, including1655 // work that is suspended. The timeout threshold cannot be longer than1656 // the overall expiration.1657 const earliestExpirationTimeMs = expirationTimeToMs(root.firstPendingTime);1658 if (earliestExpirationTimeMs < absoluteTimeoutMs) {1659 absoluteTimeoutMs = earliestExpirationTimeMs;1660 }1661 // Subtract the current time from the absolute timeout to get the number1662 // of milliseconds until the timeout. In other words, convert an absolute1663 // timestamp to a relative time. This is the value that is passed1664 // to `setTimeout`.1665 let msUntilTimeout = absoluteTimeoutMs - now();1666 return msUntilTimeout < 0 ? 0 : msUntilTimeout;1667}1668function checkForNestedUpdates() {1669 if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {1670 nestedUpdateCount = 0;1671 rootWithNestedUpdates = null;1672 invariant(1673 false,1674 'Maximum update depth exceeded. This can happen when a component ' +1675 'repeatedly calls setState inside componentWillUpdate or ' +1676 'componentDidUpdate. React limits the number of nested updates to ' +1677 'prevent infinite loops.',1678 );1679 }1680 if (__DEV__) {1681 if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {1682 nestedPassiveUpdateCount = 0;...
ReactFiberWorkLoop.js
Source:ReactFiberWorkLoop.js
...718 ensureRootIsScheduled(root, now());719 return null;720};721const scheduleUpdateOnFiber = (fiber, lane, eventTime) => {722 checkForNestedUpdates();723 const root = markUpdateLaneFromFiberToRoot(fiber, lane);724 if (root === null) return null;725 markRootUpdated(root, lane, eventTime);726 if (root === workInProgressRoot) {727 if ((executionContext & RenderContext) === NoContext) {728 workInProgressRootUpdatedLanes = mergeLanes(729 workInProgressRootUpdatedLanes,730 lane731 );732 }733 if (workInProgressRootExitStatus === RootSuspendedWithDelay) {734 markRootSuspended(root, workInProgressRootRenderLanes);735 }736 }...
scheduleUpdateOnFiber.js
Source:scheduleUpdateOnFiber.js
1function scheduleUpdateOnFiber(fiber, expirationTime) {2 checkForNestedUpdates();3 warnAboutInvalidUpdatesOnClassComponentsInDEV(fiber);4 var root = markUpdateTimeFromFiberToRoot(fiber, expirationTime);5 if (root === null) {6 warnAboutUpdateOnUnmountedFiberInDEV(fiber);7 return;8 }9 checkForInterruption(fiber, expirationTime);10 recordScheduleUpdate(); // TODO: computeExpirationForFiber also reads the priority. Pass the11 // priority as an argument to that function and this one.12 var priorityLevel = getCurrentPriorityLevel();13 if (expirationTime === Sync) {14 if ( // Check if we're inside unbatchedUpdates15 (executionContext & LegacyUnbatchedContext) !== NoContext && // Check if we're not already rendering16 (executionContext & (RenderContext | CommitContext)) === NoContext) {...
1-2__scheduleUpdateOnFiber.js
Source:1-2__scheduleUpdateOnFiber.js
1function scheduleUpdateOnFiber(fiber, expirationTime) {2 checkForNestedUpdates();3 warnAboutInvalidUpdatesOnClassComponentsInDEV(fiber);4 var root = markUpdateTimeFromFiberToRoot(fiber, expirationTime);5 if (root === null) {6 warnAboutUpdateOnUnmountedFiberInDEV(fiber);7 return;8 }9 checkForInterruption(fiber, expirationTime);10 recordScheduleUpdate(); // TODO: computeExpirationForFiber also reads the priority. Pass the11 // priority as an argument to that function and this one.12 var priorityLevel = getCurrentPriorityLevel();13 if (expirationTime === Sync) {14 if ( // Check if we're inside unbatchedUpdates15 (executionContext & LegacyUnbatchedContext) !== NoContext && // Check if we're not already rendering16 (executionContext & (RenderContext | CommitContext)) === NoContext) {...
updateEqueue.js
Source:updateEqueue.js
...46export function scheduleUpdateOnFiber(47 fiber: Fiber,48 expirationTime: ExpirationTime,49) {50 checkForNestedUpdates();51 const root = markUpdateTimeFromFiberToRoot(fiber, expirationTime);52 if (root === null) {53 warnAboutUpdateOnUnmountedFiberInDEV(fiber);54 return;55 }56 checkForInterruption(fiber, expirationTime);57 const priorityLevel = getCurrentPriorityLevel();58 if (expirationTime === Sync) {59 if (60 (executionContext & LegacyUnbatchedContext) !== NoContext &&61 (executionContext & (RenderContext | CommitContext)) === NoContext62 ) {63 schedulePendingInteractions(root, expirationTime);64 performSyncWorkOnRoot(root);...
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 await page.click('text=Sign in');7 await page.fill('input[type="email"]', 'testuser');8 await page.fill('input[type="password"]', 'testpass');9 await page.click('text=Next');10 await page.waitForNavigation();11 await page.click('text=My Account');12 await page.fill('input[type="email"]', 'testuser');13 await page.fill('input[type="password"]', 'testpass');14 await page.click('text=Next');15 await page.waitForNavigation();16 await page.click('text=Sign out');17 await page.waitForNavigation();18 await page.click('text=Sign in');19 await page.fill('input[type="email"]', 'testuser');20 await page.fill('input[type="password"]', 'testpass');21 await page.click('text=Next');22 await page.waitForNavigation();23 await page.click('text=My Account');24 await page.fill('input[type="email"]', 'testuser');25 await page.fill('input[type="password"]', 'testpass');26 await page.click('text=Next');27 await page.waitForNavigation();28 await page.click('text=Sign out');29 await page.waitForNavigation();30 await page.click('text=Sign in');31 await page.fill('input[type="email"]', 'testuser');32 await page.fill('input[type="password"]', 'testpass');33 await page.click('text=Next');34 await page.waitForNavigation();35 await page.click('text=My Account');36 await page.fill('input[type="email"]', 'testuser');37 await page.fill('input[type="password"]', 'testpass');38 await page.click('text=Next');39 await page.waitForNavigation();40 await page.click('text=Sign out');41 await page.waitForNavigation();42 await page.click('text=Sign in');43 await page.fill('input[type="email"]', 'testuser');44 await page.fill('input[type="password"]', 'testpass');45 await page.click('text=Next');46 await page.waitForNavigation();47 await page.click('text=My Account');
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 await page.waitForSelector('input[title="Search"]');7 await page.fill('input[title="Search"]', 'Hello World');8 await page.keyboard.press('Enter');9 await page.waitForSelector('h3');10 const innerHTML = await page.innerHTML('h3');11 console.log(innerHTML);12 await browser.close();13})();
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const page = await browser.newPage();5 await page.waitForTimeout(10000);6 await page.click('text=Get Started');7 await page.waitForTimeout(10000);8 await page.click('text=Docs');9 await page.waitForTimeout(10000);10 await page.click('text=API');11 await page.waitForTimeout(10000);12 await page.click('text=Internal API');13 await page.waitForTimeout(10000);14 await page.click('text=checkForNestedUpdates');15 await page.waitForTimeout(10000);16 await page.click('text=Docs');17 await page.waitForTimeout(10000);18 await page.click('text=API');19 await page.waitForTimeout(10000);20 await page.click('text=Internal API');21 await page.waitForTimeout(10000);22 await page.click('text=checkForNestedUpdates');23 await page.waitForTimeout(10000);24 await page.click('text=Docs');25 await page.waitForTimeout(10000);26 await page.click('text=API');27 await page.waitForTimeout(10000);28 await page.click('text=Internal API');29 await page.waitForTimeout(10000);30 await page.click('text=checkForNestedUpdates');31 await page.waitForTimeout(10000);32 await page.click('text=Docs');33 await page.waitForTimeout(10000);34 await page.click('text=API');35 await page.waitForTimeout(10000);36 await page.click('text=Internal API');37 await page.waitForTimeout(10000);38 await page.click('text=checkForNestedUpdates');39 await page.waitForTimeout(10000);40 await page.click('text=Docs');41 await page.waitForTimeout(10000);42 await page.click('text=API');43 await page.waitForTimeout(10000);44 await page.click('text=Internal API');45 await page.waitForTimeout(10000);46 await page.click('text=checkForNestedUpdates');47 await page.waitForTimeout(10000);48 await page.click('text=Docs');49 await page.waitForTimeout(10000);50 await page.click('text=API');51 await page.waitForTimeout(10000);52 await page.click('text=Internal API');
Using AI Code Generation
1const { chromium } = require('playwright');2const { checkForNestedUpdates } = require('playwright/lib/internal');3(async () => {4 const browser = await chromium.launch();5 const context = await browser.newContext();6 const page = await context.newPage();7 await page.waitForSelector('input[name="q"]');8 await page.type('input[name="q"]', 'Playwright');9 await page.keyboard.press('Enter');10 await page.waitForSelector('text=Playwright - Google Search');11 console.log(await checkForNestedUpdates(page));12 await browser.close();13})();14{ hasNestedUpdates: false, nestedUpdates: [] }15{ hasNestedUpdates: true,16 [ { target: 'input[name="q"]',17 selectionDirection: 'forward' },18 { target: 'input[name="q"]',19 selectionDirection: 'forward' },20 { target: 'input[name="q"]',21 selectionDirection: 'forward' },22 { target: 'input[name="q"]',23 selectionDirection: 'forward' },24 { target: 'input[name="q"]',25 selectionDirection: 'forward' },26 { target: 'input[name="q"]',27 selectionDirection: 'forward' },28 { target: 'input[name="q"]',29 selectionDirection: 'forward' },30 { target: 'input[name="q
Using AI Code Generation
1const { checkForNestedUpdates } = require('playwright/lib/server/supplements/recorder/recorderSupplement');2const { chromium } = require('playwright');3(async () => {4 const browser = await chromium.launch();5 const context = await browser.newContext();6 const page = await context.newPage();7 await checkForNestedUpdates(page);8 await browser.close();9})();10{11 {12 "state": {}13 }14}
Using AI Code Generation
1const { checkForNestedUpdates } = require('playwright/lib/server/supplements/recorder/recorderSupplement');2const { chromium } = require('playwright');3(async () => {4 const browser = await chromium.launch();5 const page = await browser.newPage();6 await page.click('text="Get Started"');7 const nestedUpdates = await checkForNestedUpdates(page);8 console.log(nestedUpdates);9 await browser.close();10})();
Using AI Code Generation
1const { checkForNestedUpdates } = require('playwright/lib/server/chromium/crPage');2const { BrowserContext } = require('playwright/lib/server/browserContext');3const { Page } = require('playwright/lib/server/page');4const { checkForNestedUpdates } = require('playwright/lib/server/chromium/crPage');5const { BrowserContext } = require('playwright/lib/server/browserContext');6const { Page } = require('playwright/lib/server/page');7const { checkForNestedUpdates } = require('playwright/lib/server/chromium/crPage');8const { BrowserContext } = require('playwright/lib/server/browserContext');9const { Page } = require('playwright/lib/server/page');10const { checkForNestedUpdates } = require('playwright/lib/server/chromium/crPage');11const { BrowserContext } = require('playwright/lib/server/browserContext');12const { Page } = require('playwright/lib/server/page');13const { checkForNestedUpdates } = require('playwright/lib/server/chromium/crPage');14const { BrowserContext } = require('playwright/lib/server/browserContext');15const { Page } = require('playwright/lib/server/page');16const { checkForNestedUpdates } = require('playwright/lib/server/chromium/crPage');17const { BrowserContext } = require('playwright/lib/server/browserContext');18const { Page } = require('playwright/lib/server/page');19const { checkForNestedUpdates } = require('playwright/lib/server/chromium/crPage');20const { BrowserContext } = require('playwright/lib/server/browserContext');21const { Page } = require('playwright/lib/server/page');22const { checkForNestedUpdates } = require('playwright/lib/server/chromium/crPage');23const { BrowserContext } = require('playwright/lib/server/browserContext');24const { Page } = require('playwright/lib/server/page');
Using AI Code Generation
1const { checkForNestedUpdates } = require('playwright/lib/internal/inspector');2const { chromium } = require('playwright');3(async () => {4 const browser = await chromium.launch();5 const page = await browser.newPage();6 await page.evaluate(() => {7 document.body.innerHTML = '<div>foo</div>';8 });9 const updates = await checkForNestedUpdates(page);10 console.log(updates);11 await browser.close();12})();13 {14 { id: 'body', children: [ { id: 'div', children: [] } ] }15 }
Using AI Code Generation
1const { Internal } = require('playwright');2const { checkForNestedUpdates } = Internal;3const obj1 = {4 b: {5 }6}7const obj2 = {8 b: {9 }10}11const obj3 = {12 b: {13 }14}
Using AI Code Generation
1const { checkForNestedUpdates } = require('playwright/lib/internal/utils/utils');2const { test } = require('playwright');3const { expect } = require('expect');4test('test', async ({ page }) => {5 const result = checkForNestedUpdates();6 console.log(result);7});8await page.click('text=Get started');9const result = checkForNestedUpdates();10console.log(result);11const { checkForUpdates } = require('playwright/lib/internal/utils/utils');12const { test } = require('playwright');13const { expect } = require('expect');14test('test', async ({ page }) => {15 const result = checkForUpdates();16 console.log(result);17});18await page.click('text=Get started');19const result = checkForUpdates();20console.log(result);
LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!