Best JavaScript code snippet using playwright-internal
ReactFiberWorkLoop.old.js
Source:ReactFiberWorkLoop.old.js
...491 didScheduleUpdateDuringPassiveEffects = true;492 }493 }494 // Mark that the root has a pending update.495 markRootUpdated(root, lane, eventTime);496 if (497 (executionContext & RenderContext) !== NoLanes &&498 root === workInProgressRoot499 ) {500 // This update was dispatched during the render phase. This is a mistake501 // if the update originates from user space (with the exception of local502 // hook updates, which are handled differently and don't reach this503 // function), but there are some internal React features that use this as504 // an implementation detail, like selective hydration.505 warnAboutRenderPhaseUpdatesInDEV(fiber);506 // Track lanes that were updated during the render phase507 workInProgressRootRenderPhaseUpdatedLanes = mergeLanes(508 workInProgressRootRenderPhaseUpdatedLanes,509 lane,510 );511 } else {512 // This is a normal update, scheduled from outside the render phase. For513 // example, during an input event.514 if (enableUpdaterTracking) {515 if (isDevToolsPresent) {516 addFiberToLanesMap(root, fiber, lane);517 }518 }519 warnIfUpdatesNotWrappedWithActDEV(fiber);520 if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {521 if (522 (executionContext & CommitContext) !== NoContext &&523 root === rootCommittingMutationOrLayoutEffects524 ) {525 if (fiber.mode & ProfileMode) {526 let current = fiber;527 while (current !== null) {528 if (current.tag === Profiler) {529 const {id, onNestedUpdateScheduled} = current.memoizedProps;530 if (typeof onNestedUpdateScheduled === 'function') {531 onNestedUpdateScheduled(id);532 }533 }534 current = current.return;535 }536 }537 }538 }539 if (enableTransitionTracing) {540 const transition = ReactCurrentBatchConfig.transition;541 if (transition !== null) {542 if (transition.startTime === -1) {543 transition.startTime = now();544 }545 addTransitionToLanesMap(root, transition, lane);546 }547 }548 if (root === workInProgressRoot) {549 // TODO: Consolidate with `isInterleavedUpdate` check550 // Received an update to a tree that's in the middle of rendering. Mark551 // that there was an interleaved update work on this root. Unless the552 // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render553 // phase update. In that case, we don't treat render phase updates as if554 // they were interleaved, for backwards compat reasons.555 if (556 deferRenderPhaseUpdateToNextBatch ||557 (executionContext & RenderContext) === NoContext558 ) {559 workInProgressRootInterleavedUpdatedLanes = mergeLanes(560 workInProgressRootInterleavedUpdatedLanes,561 lane,562 );563 }564 if (workInProgressRootExitStatus === RootSuspendedWithDelay) {565 // The root already suspended with a delay, which means this render566 // definitely won't finish. Since we have a new update, let's mark it as567 // suspended now, right before marking the incoming update. This has the568 // effect of interrupting the current render and switching to the update.569 // TODO: Make sure this doesn't override pings that happen while we've570 // already started rendering.571 markRootSuspended(root, workInProgressRootRenderLanes);572 }573 }574 ensureRootIsScheduled(root, eventTime);575 if (576 lane === SyncLane &&577 executionContext === NoContext &&578 (fiber.mode & ConcurrentMode) === NoMode &&579 // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.580 !(__DEV__ && ReactCurrentActQueue.isBatchingLegacy)581 ) {582 // Flush the synchronous work now, unless we're already working or inside583 // a batch. This is intentionally inside scheduleUpdateOnFiber instead of584 // scheduleCallbackForFiber to preserve the ability to schedule a callback585 // without immediately flushing it. We only do this for user-initiated586 // updates, to preserve historical behavior of legacy mode.587 resetRenderTimer();588 flushSyncCallbacksOnlyInLegacyMode();589 }590 }591 return root;592}593export function scheduleInitialHydrationOnRoot(594 root: FiberRoot,595 lane: Lane,596 eventTime: number,597) {598 // This is a special fork of scheduleUpdateOnFiber that is only used to599 // schedule the initial hydration of a root that has just been created. Most600 // of the stuff in scheduleUpdateOnFiber can be skipped.601 //602 // The main reason for this separate path, though, is to distinguish the603 // initial children from subsequent updates. In fully client-rendered roots604 // (createRoot instead of hydrateRoot), all top-level renders are modeled as605 // updates, but hydration roots are special because the initial render must606 // match what was rendered on the server.607 const current = root.current;608 current.lanes = lane;609 markRootUpdated(root, lane, eventTime);610 ensureRootIsScheduled(root, eventTime);611}612// This is split into a separate function so we can mark a fiber with pending613// work without treating it as a typical update that originates from an event;614// e.g. retrying a Suspense boundary isn't an update, but it does schedule work615// on a fiber.616function markUpdateLaneFromFiberToRoot(617 sourceFiber: Fiber,618 lane: Lane,619): FiberRoot | null {620 // Update the source fiber's lanes621 sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);622 let alternate = sourceFiber.alternate;623 if (alternate !== null) {624 alternate.lanes = mergeLanes(alternate.lanes, lane);625 }626 if (__DEV__) {627 if (628 alternate === null &&629 (sourceFiber.flags & (Placement | Hydrating)) !== NoFlags630 ) {631 warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);632 }633 }634 // Walk the parent path to the root and update the child lanes.635 let node = sourceFiber;636 let parent = sourceFiber.return;637 while (parent !== null) {638 parent.childLanes = mergeLanes(parent.childLanes, lane);639 alternate = parent.alternate;640 if (alternate !== null) {641 alternate.childLanes = mergeLanes(alternate.childLanes, lane);642 } else {643 if (__DEV__) {644 if ((parent.flags & (Placement | Hydrating)) !== NoFlags) {645 warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);646 }647 }648 }649 node = parent;650 parent = parent.return;651 }652 if (node.tag === HostRoot) {653 const root: FiberRoot = node.stateNode;654 return root;655 } else {656 return null;657 }658}659export function isInterleavedUpdate(fiber: Fiber, lane: Lane) {660 return (661 // TODO: Optimize slightly by comparing to root that fiber belongs to.662 // Requires some refactoring. Not a big deal though since it's rare for663 // concurrent apps to have more than a single root.664 workInProgressRoot !== null &&665 (fiber.mode & ConcurrentMode) !== NoMode &&666 // If this is a render phase update (i.e. UNSAFE_componentWillReceiveProps),667 // then don't treat this as an interleaved update. This pattern is668 // accompanied by a warning but we haven't fully deprecated it yet. We can669 // remove once the deferRenderPhaseUpdateToNextBatch flag is enabled.670 (deferRenderPhaseUpdateToNextBatch ||671 (executionContext & RenderContext) === NoContext)672 );673}674// Use this function to schedule a task for a root. There's only one task per675// root; if a task was already scheduled, we'll check to make sure the priority676// of the existing task is the same as the priority of the next level that the677// root has work on. This function is called on every update, and right before678// exiting a task.679function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {680 const existingCallbackNode = root.callbackNode;681 // Check if any lanes are being starved by other work. If so, mark them as682 // expired so we know to work on those next.683 markStarvedLanesAsExpired(root, currentTime);684 // Determine the next lanes to work on, and their priority.685 const nextLanes = getNextLanes(686 root,687 root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,688 );689 if (nextLanes === NoLanes) {690 // Special case: There's nothing to work on.691 if (existingCallbackNode !== null) {692 cancelCallback(existingCallbackNode);693 }694 root.callbackNode = null;695 root.callbackPriority = NoLane;696 return;697 }698 // We use the highest priority lane to represent the priority of the callback.699 const newCallbackPriority = getHighestPriorityLane(nextLanes);700 // Check if there's an existing task. We may be able to reuse it.701 const existingCallbackPriority = root.callbackPriority;702 if (703 existingCallbackPriority === newCallbackPriority &&704 // Special case related to `act`. If the currently scheduled task is a705 // Scheduler task, rather than an `act` task, cancel it and re-scheduled706 // on the `act` queue.707 !(708 __DEV__ &&709 ReactCurrentActQueue.current !== null &&710 existingCallbackNode !== fakeActCallbackNode711 )712 ) {713 if (__DEV__) {714 // If we're going to re-use an existing task, it needs to exist.715 // Assume that discrete update microtasks are non-cancellable and null.716 // TODO: Temporary until we confirm this warning is not fired.717 if (718 existingCallbackNode == null &&719 existingCallbackPriority !== SyncLane720 ) {721 console.error(722 'Expected scheduled callback to exist. This error is likely caused by a bug in React. Please file an issue.',723 );724 }725 }726 // The priority hasn't changed. We can reuse the existing task. Exit.727 return;728 }729 if (existingCallbackNode != null) {730 // Cancel the existing callback. We'll schedule a new one below.731 cancelCallback(existingCallbackNode);732 }733 // Schedule a new callback.734 let newCallbackNode;735 if (newCallbackPriority === SyncLane) {736 // Special case: Sync React callbacks are scheduled on a special737 // internal queue738 if (root.tag === LegacyRoot) {739 if (__DEV__ && ReactCurrentActQueue.isBatchingLegacy !== null) {740 ReactCurrentActQueue.didScheduleLegacyUpdate = true;741 }742 scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root));743 } else {744 scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));745 }746 if (supportsMicrotasks) {747 // Flush the queue in a microtask.748 if (__DEV__ && ReactCurrentActQueue.current !== null) {749 // Inside `act`, use our internal `act` queue so that these get flushed750 // at the end of the current scope even when using the sync version751 // of `act`.752 ReactCurrentActQueue.current.push(flushSyncCallbacks);753 } else {754 scheduleMicrotask(() => {755 // In Safari, appending an iframe forces microtasks to run.756 // https://github.com/facebook/react/issues/22459757 // We don't support running callbacks in the middle of render758 // or commit so we need to check against that.759 if (executionContext === NoContext) {760 // It's only safe to do this conditionally because we always761 // check for pending work before we exit the task.762 flushSyncCallbacks();763 }764 });765 }766 } else {767 // Flush the queue in an Immediate task.768 scheduleCallback(ImmediateSchedulerPriority, flushSyncCallbacks);769 }770 newCallbackNode = null;771 } else {772 let schedulerPriorityLevel;773 switch (lanesToEventPriority(nextLanes)) {774 case DiscreteEventPriority:775 schedulerPriorityLevel = ImmediateSchedulerPriority;776 break;777 case ContinuousEventPriority:778 schedulerPriorityLevel = UserBlockingSchedulerPriority;779 break;780 case DefaultEventPriority:781 schedulerPriorityLevel = NormalSchedulerPriority;782 break;783 case IdleEventPriority:784 schedulerPriorityLevel = IdleSchedulerPriority;785 break;786 default:787 schedulerPriorityLevel = NormalSchedulerPriority;788 break;789 }790 newCallbackNode = scheduleCallback(791 schedulerPriorityLevel,792 performConcurrentWorkOnRoot.bind(null, root),793 );794 }795 root.callbackPriority = newCallbackPriority;796 root.callbackNode = newCallbackNode;797}798// This is the entry point for every concurrent task, i.e. anything that799// goes through Scheduler.800function performConcurrentWorkOnRoot(root, didTimeout) {801 if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {802 resetNestedUpdateFlag();803 }804 // Since we know we're in a React event, we can clear the current805 // event time. The next update will compute a new event time.806 currentEventTime = NoTimestamp;807 currentEventTransitionLane = NoLanes;808 if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {809 throw new Error('Should not already be working.');810 }811 // Flush any pending passive effects before deciding which lanes to work on,812 // in case they schedule additional work.813 const originalCallbackNode = root.callbackNode;814 const didFlushPassiveEffects = flushPassiveEffects();815 if (didFlushPassiveEffects) {816 // Something in the passive effect phase may have canceled the current task.817 // Check if the task node for this root was changed.818 if (root.callbackNode !== originalCallbackNode) {819 // The current task was canceled. Exit. We don't need to call820 // `ensureRootIsScheduled` because the check above implies either that821 // there's a new task, or that there's no remaining work on this root.822 return null;823 } else {824 // Current task was not canceled. Continue.825 }826 }827 // Determine the next lanes to work on, using the fields stored828 // on the root.829 let lanes = getNextLanes(830 root,831 root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,832 );833 if (lanes === NoLanes) {834 // Defensive coding. This is never expected to happen.835 return null;836 }837 // We disable time-slicing in some cases: if the work has been CPU-bound838 // for too long ("expired" work, to prevent starvation), or we're in839 // sync-updates-by-default mode.840 // TODO: We only check `didTimeout` defensively, to account for a Scheduler841 // bug we're still investigating. Once the bug in Scheduler is fixed,842 // we can remove this, since we track expiration ourselves.843 const shouldTimeSlice =844 !includesBlockingLane(root, lanes) &&845 !includesExpiredLane(root, lanes) &&846 (disableSchedulerTimeoutInWorkLoop || !didTimeout);847 let exitStatus = shouldTimeSlice848 ? renderRootConcurrent(root, lanes)849 : renderRootSync(root, lanes);850 if (exitStatus !== RootInProgress) {851 if (exitStatus === RootErrored) {852 // If something threw an error, try rendering one more time. We'll853 // render synchronously to block concurrent data mutations, and we'll854 // includes all pending updates are included. If it still fails after855 // the second attempt, we'll give up and commit the resulting tree.856 const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);857 if (errorRetryLanes !== NoLanes) {858 lanes = errorRetryLanes;859 exitStatus = recoverFromConcurrentError(root, errorRetryLanes);860 }861 }862 if (exitStatus === RootFatalErrored) {863 const fatalError = workInProgressRootFatalError;864 prepareFreshStack(root, NoLanes);865 markRootSuspended(root, lanes);866 ensureRootIsScheduled(root, now());867 throw fatalError;868 }869 if (exitStatus === RootDidNotComplete) {870 // The render unwound without completing the tree. This happens in special871 // cases where need to exit the current render without producing a872 // consistent tree or committing.873 //874 // This should only happen during a concurrent render, not a discrete or875 // synchronous update. We should have already checked for this when we876 // unwound the stack.877 markRootSuspended(root, lanes);878 } else {879 // The render completed.880 // Check if this render may have yielded to a concurrent event, and if so,881 // confirm that any newly rendered stores are consistent.882 // TODO: It's possible that even a concurrent render may never have yielded883 // to the main thread, if it was fast enough, or if it expired. We could884 // skip the consistency check in that case, too.885 const renderWasConcurrent = !includesBlockingLane(root, lanes);886 const finishedWork: Fiber = (root.current.alternate: any);887 if (888 renderWasConcurrent &&889 !isRenderConsistentWithExternalStores(finishedWork)890 ) {891 // A store was mutated in an interleaved event. Render again,892 // synchronously, to block further mutations.893 exitStatus = renderRootSync(root, lanes);894 // We need to check again if something threw895 if (exitStatus === RootErrored) {896 const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);897 if (errorRetryLanes !== NoLanes) {898 lanes = errorRetryLanes;899 exitStatus = recoverFromConcurrentError(root, errorRetryLanes);900 // We assume the tree is now consistent because we didn't yield to any901 // concurrent events.902 }903 }904 if (exitStatus === RootFatalErrored) {905 const fatalError = workInProgressRootFatalError;906 prepareFreshStack(root, NoLanes);907 markRootSuspended(root, lanes);908 ensureRootIsScheduled(root, now());909 throw fatalError;910 }911 }912 // We now have a consistent tree. The next step is either to commit it,913 // or, if something suspended, wait to commit it after a timeout.914 root.finishedWork = finishedWork;915 root.finishedLanes = lanes;916 finishConcurrentRender(root, exitStatus, lanes);917 }918 }919 ensureRootIsScheduled(root, now());920 if (root.callbackNode === originalCallbackNode) {921 // The task node scheduled for this root is the same one that's922 // currently executed. Need to return a continuation.923 return performConcurrentWorkOnRoot.bind(null, root);924 }925 return null;926}927function recoverFromConcurrentError(root, errorRetryLanes) {928 // If an error occurred during hydration, discard server response and fall929 // back to client side render.930 // Before rendering again, save the errors from the previous attempt.931 const errorsFromFirstAttempt = workInProgressRootConcurrentErrors;932 if (isRootDehydrated(root)) {933 // The shell failed to hydrate. Set a flag to force a client rendering934 // during the next attempt. To do this, we call prepareFreshStack now935 // to create the root work-in-progress fiber. This is a bit weird in terms936 // of factoring, because it relies on renderRootSync not calling937 // prepareFreshStack again in the call below, which happens because the938 // root and lanes haven't changed.939 //940 // TODO: I think what we should do is set ForceClientRender inside941 // throwException, like we do for nested Suspense boundaries. The reason942 // it's here instead is so we can switch to the synchronous work loop, too.943 // Something to consider for a future refactor.944 const rootWorkInProgress = prepareFreshStack(root, errorRetryLanes);945 rootWorkInProgress.flags |= ForceClientRender;946 if (__DEV__) {947 errorHydratingContainer(root.containerInfo);948 }949 }950 const exitStatus = renderRootSync(root, errorRetryLanes);951 if (exitStatus !== RootErrored) {952 // Successfully finished rendering on retry953 // The errors from the failed first attempt have been recovered. Add954 // them to the collection of recoverable errors. We'll log them in the955 // commit phase.956 const errorsFromSecondAttempt = workInProgressRootRecoverableErrors;957 workInProgressRootRecoverableErrors = errorsFromFirstAttempt;958 // The errors from the second attempt should be queued after the errors959 // from the first attempt, to preserve the causal sequence.960 if (errorsFromSecondAttempt !== null) {961 queueRecoverableErrors(errorsFromSecondAttempt);962 }963 } else {964 // The UI failed to recover.965 }966 return exitStatus;967}968export function queueRecoverableErrors(errors: Array<mixed>) {969 if (workInProgressRootRecoverableErrors === null) {970 workInProgressRootRecoverableErrors = errors;971 } else {972 workInProgressRootRecoverableErrors.push.apply(973 workInProgressRootRecoverableErrors,974 errors,975 );976 }977}978function finishConcurrentRender(root, exitStatus, lanes) {979 switch (exitStatus) {980 case RootInProgress:981 case RootFatalErrored: {982 throw new Error('Root did not complete. This is a bug in React.');983 }984 // Flow knows about invariant, so it complains if I add a break985 // statement, but eslint doesn't know about invariant, so it complains986 // if I do. eslint-disable-next-line no-fallthrough987 case RootErrored: {988 // We should have already attempted to retry this tree. If we reached989 // this point, it errored again. Commit it.990 commitRoot(991 root,992 workInProgressRootRecoverableErrors,993 workInProgressTransitions,994 );995 break;996 }997 case RootSuspended: {998 markRootSuspended(root, lanes);999 // We have an acceptable loading state. We need to figure out if we1000 // should immediately commit it or wait a bit.1001 if (1002 includesOnlyRetries(lanes) &&1003 // do not delay if we're inside an act() scope1004 !shouldForceFlushFallbacksInDEV()1005 ) {1006 // This render only included retries, no updates. Throttle committing1007 // retries so that we don't show too many loading states too quickly.1008 const msUntilTimeout =1009 globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now();1010 // Don't bother with a very short suspense time.1011 if (msUntilTimeout > 10) {1012 const nextLanes = getNextLanes(root, NoLanes);1013 if (nextLanes !== NoLanes) {1014 // There's additional work on this root.1015 break;1016 }1017 const suspendedLanes = root.suspendedLanes;1018 if (!isSubsetOfLanes(suspendedLanes, lanes)) {1019 // We should prefer to render the fallback of at the last1020 // suspended level. Ping the last suspended level to try1021 // rendering it again.1022 // FIXME: What if the suspended lanes are Idle? Should not restart.1023 const eventTime = requestEventTime();1024 markRootPinged(root, suspendedLanes, eventTime);1025 break;1026 }1027 // The render is suspended, it hasn't timed out, and there's no1028 // lower priority work to do. Instead of committing the fallback1029 // immediately, wait for more data to arrive.1030 root.timeoutHandle = scheduleTimeout(1031 commitRoot.bind(1032 null,1033 root,1034 workInProgressRootRecoverableErrors,1035 workInProgressTransitions,1036 ),1037 msUntilTimeout,1038 );1039 break;1040 }1041 }1042 // The work expired. Commit immediately.1043 commitRoot(1044 root,1045 workInProgressRootRecoverableErrors,1046 workInProgressTransitions,1047 );1048 break;1049 }1050 case RootSuspendedWithDelay: {1051 markRootSuspended(root, lanes);1052 if (includesOnlyNonUrgentLanes(lanes)) {1053 // This is a transition, so we should exit without committing a1054 // placeholder and without scheduling a timeout. Delay indefinitely1055 // until we receive more data.1056 break;1057 }1058 if (!shouldForceFlushFallbacksInDEV()) {1059 // This is not a transition, but we did trigger an avoided state.1060 // Schedule a placeholder to display after a short delay, using the Just1061 // Noticeable Difference.1062 // TODO: Is the JND optimization worth the added complexity? If this is1063 // the only reason we track the event time, then probably not.1064 // Consider removing.1065 const mostRecentEventTime = getMostRecentEventTime(root, lanes);1066 const eventTimeMs = mostRecentEventTime;1067 const timeElapsedMs = now() - eventTimeMs;1068 const msUntilTimeout = jnd(timeElapsedMs) - timeElapsedMs;1069 // Don't bother with a very short suspense time.1070 if (msUntilTimeout > 10) {1071 // Instead of committing the fallback immediately, wait for more data1072 // to arrive.1073 root.timeoutHandle = scheduleTimeout(1074 commitRoot.bind(1075 null,1076 root,1077 workInProgressRootRecoverableErrors,1078 workInProgressTransitions,1079 ),1080 msUntilTimeout,1081 );1082 break;1083 }1084 }1085 // Commit the placeholder.1086 commitRoot(1087 root,1088 workInProgressRootRecoverableErrors,1089 workInProgressTransitions,1090 );1091 break;1092 }1093 case RootCompleted: {1094 // The work completed. Ready to commit.1095 commitRoot(1096 root,1097 workInProgressRootRecoverableErrors,1098 workInProgressTransitions,1099 );1100 break;1101 }1102 default: {1103 throw new Error('Unknown root exit status.');1104 }1105 }1106}1107function isRenderConsistentWithExternalStores(finishedWork: Fiber): boolean {1108 // Search the rendered tree for external store reads, and check whether the1109 // stores were mutated in a concurrent event. Intentionally using an iterative1110 // loop instead of recursion so we can exit early.1111 let node: Fiber = finishedWork;1112 while (true) {1113 if (node.flags & StoreConsistency) {1114 const updateQueue: FunctionComponentUpdateQueue | null = (node.updateQueue: any);1115 if (updateQueue !== null) {1116 const checks = updateQueue.stores;1117 if (checks !== null) {1118 for (let i = 0; i < checks.length; i++) {1119 const check = checks[i];1120 const getSnapshot = check.getSnapshot;1121 const renderedValue = check.value;1122 try {1123 if (!is(getSnapshot(), renderedValue)) {1124 // Found an inconsistent store.1125 return false;1126 }1127 } catch (error) {1128 // If `getSnapshot` throws, return `false`. This will schedule1129 // a re-render, and the error will be rethrown during render.1130 return false;1131 }1132 }1133 }1134 }1135 }1136 const child = node.child;1137 if (node.subtreeFlags & StoreConsistency && child !== null) {1138 child.return = node;1139 node = child;1140 continue;1141 }1142 if (node === finishedWork) {1143 return true;1144 }1145 while (node.sibling === null) {1146 if (node.return === null || node.return === finishedWork) {1147 return true;1148 }1149 node = node.return;1150 }1151 node.sibling.return = node.return;1152 node = node.sibling;1153 }1154 // Flow doesn't know this is unreachable, but eslint does1155 // eslint-disable-next-line no-unreachable1156 return true;1157}1158function markRootSuspended(root, suspendedLanes) {1159 // When suspending, we should always exclude lanes that were pinged or (more1160 // rarely, since we try to avoid it) updated during the render phase.1161 // TODO: Lol maybe there's a better way to factor this besides this1162 // obnoxiously named function :)1163 suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);1164 suspendedLanes = removeLanes(1165 suspendedLanes,1166 workInProgressRootInterleavedUpdatedLanes,1167 );1168 markRootSuspended_dontCallThisOneDirectly(root, suspendedLanes);1169}1170// This is the entry point for synchronous tasks that don't go1171// through Scheduler1172function performSyncWorkOnRoot(root) {1173 if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {1174 syncNestedUpdateFlag();1175 }1176 if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {1177 throw new Error('Should not already be working.');1178 }1179 flushPassiveEffects();1180 let lanes = getNextLanes(root, NoLanes);1181 if (!includesSomeLane(lanes, SyncLane)) {1182 // There's no remaining sync work left.1183 ensureRootIsScheduled(root, now());1184 return null;1185 }1186 let exitStatus = renderRootSync(root, lanes);1187 if (root.tag !== LegacyRoot && exitStatus === RootErrored) {1188 // If something threw an error, try rendering one more time. We'll render1189 // synchronously to block concurrent data mutations, and we'll includes1190 // all pending updates are included. If it still fails after the second1191 // attempt, we'll give up and commit the resulting tree.1192 const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);1193 if (errorRetryLanes !== NoLanes) {1194 lanes = errorRetryLanes;1195 exitStatus = recoverFromConcurrentError(root, errorRetryLanes);1196 }1197 }1198 if (exitStatus === RootFatalErrored) {1199 const fatalError = workInProgressRootFatalError;1200 prepareFreshStack(root, NoLanes);1201 markRootSuspended(root, lanes);1202 ensureRootIsScheduled(root, now());1203 throw fatalError;1204 }1205 if (exitStatus === RootDidNotComplete) {1206 throw new Error('Root did not complete. This is a bug in React.');1207 }1208 // We now have a consistent tree. Because this is a sync render, we1209 // will commit it even if something suspended.1210 const finishedWork: Fiber = (root.current.alternate: any);1211 root.finishedWork = finishedWork;1212 root.finishedLanes = lanes;1213 commitRoot(1214 root,1215 workInProgressRootRecoverableErrors,1216 workInProgressTransitions,1217 );1218 // Before exiting, make sure there's a callback scheduled for the next1219 // pending level.1220 ensureRootIsScheduled(root, now());1221 return null;1222}1223export function flushRoot(root: FiberRoot, lanes: Lanes) {1224 if (lanes !== NoLanes) {1225 markRootEntangled(root, mergeLanes(lanes, SyncLane));1226 ensureRootIsScheduled(root, now());1227 if ((executionContext & (RenderContext | CommitContext)) === NoContext) {1228 resetRenderTimer();1229 flushSyncCallbacks();1230 }1231 }1232}1233export function getExecutionContext(): ExecutionContext {1234 return executionContext;1235}1236export function deferredUpdates<A>(fn: () => A): A {1237 const previousPriority = getCurrentUpdatePriority();1238 const prevTransition = ReactCurrentBatchConfig.transition;1239 try {1240 ReactCurrentBatchConfig.transition = null;1241 setCurrentUpdatePriority(DefaultEventPriority);1242 return fn();1243 } finally {1244 setCurrentUpdatePriority(previousPriority);1245 ReactCurrentBatchConfig.transition = prevTransition;1246 }1247}1248export function batchedUpdates<A, R>(fn: A => R, a: A): R {1249 const prevExecutionContext = executionContext;1250 executionContext |= BatchedContext;1251 try {1252 return fn(a);1253 } finally {1254 executionContext = prevExecutionContext;1255 // If there were legacy sync updates, flush them at the end of the outer1256 // most batchedUpdates-like method.1257 if (1258 executionContext === NoContext &&1259 // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.1260 !(__DEV__ && ReactCurrentActQueue.isBatchingLegacy)1261 ) {1262 resetRenderTimer();1263 flushSyncCallbacksOnlyInLegacyMode();1264 }1265 }1266}1267export function discreteUpdates<A, B, C, D, R>(1268 fn: (A, B, C, D) => R,1269 a: A,1270 b: B,1271 c: C,1272 d: D,1273): R {1274 const previousPriority = getCurrentUpdatePriority();1275 const prevTransition = ReactCurrentBatchConfig.transition;1276 try {1277 ReactCurrentBatchConfig.transition = null;1278 setCurrentUpdatePriority(DiscreteEventPriority);1279 return fn(a, b, c, d);1280 } finally {1281 setCurrentUpdatePriority(previousPriority);1282 ReactCurrentBatchConfig.transition = prevTransition;1283 if (executionContext === NoContext) {1284 resetRenderTimer();1285 }1286 }1287}1288// Overload the definition to the two valid signatures.1289// Warning, this opts-out of checking the function body.1290declare function flushSync<R>(fn: () => R): R;1291// eslint-disable-next-line no-redeclare1292declare function flushSync(): void;1293// eslint-disable-next-line no-redeclare1294export function flushSync(fn) {1295 // In legacy mode, we flush pending passive effects at the beginning of the1296 // next event, not at the end of the previous one.1297 if (1298 rootWithPendingPassiveEffects !== null &&1299 rootWithPendingPassiveEffects.tag === LegacyRoot &&1300 (executionContext & (RenderContext | CommitContext)) === NoContext1301 ) {1302 flushPassiveEffects();1303 }1304 const prevExecutionContext = executionContext;1305 executionContext |= BatchedContext;1306 const prevTransition = ReactCurrentBatchConfig.transition;1307 const previousPriority = getCurrentUpdatePriority();1308 try {1309 ReactCurrentBatchConfig.transition = null;1310 setCurrentUpdatePriority(DiscreteEventPriority);1311 if (fn) {1312 return fn();1313 } else {1314 return undefined;1315 }1316 } finally {1317 setCurrentUpdatePriority(previousPriority);1318 ReactCurrentBatchConfig.transition = prevTransition;1319 executionContext = prevExecutionContext;1320 // Flush the immediate callbacks that were scheduled during this batch.1321 // Note that this will happen even if batchedUpdates is higher up1322 // the stack.1323 if ((executionContext & (RenderContext | CommitContext)) === NoContext) {1324 flushSyncCallbacks();1325 }1326 }1327}1328export function isAlreadyRendering() {1329 // Used by the renderer to print a warning if certain APIs are called from1330 // the wrong context.1331 return (1332 __DEV__ &&1333 (executionContext & (RenderContext | CommitContext)) !== NoContext1334 );1335}1336export function flushControlled(fn: () => mixed): void {1337 const prevExecutionContext = executionContext;1338 executionContext |= BatchedContext;1339 const prevTransition = ReactCurrentBatchConfig.transition;1340 const previousPriority = getCurrentUpdatePriority();1341 try {1342 ReactCurrentBatchConfig.transition = null;1343 setCurrentUpdatePriority(DiscreteEventPriority);1344 fn();1345 } finally {1346 setCurrentUpdatePriority(previousPriority);1347 ReactCurrentBatchConfig.transition = prevTransition;1348 executionContext = prevExecutionContext;1349 if (executionContext === NoContext) {1350 // Flush the immediate callbacks that were scheduled during this batch1351 resetRenderTimer();1352 flushSyncCallbacks();1353 }1354 }1355}1356export function pushRenderLanes(fiber: Fiber, lanes: Lanes) {1357 pushToStack(subtreeRenderLanesCursor, subtreeRenderLanes, fiber);1358 subtreeRenderLanes = mergeLanes(subtreeRenderLanes, lanes);1359 workInProgressRootIncludedLanes = mergeLanes(1360 workInProgressRootIncludedLanes,1361 lanes,1362 );1363}1364export function popRenderLanes(fiber: Fiber) {1365 subtreeRenderLanes = subtreeRenderLanesCursor.current;1366 popFromStack(subtreeRenderLanesCursor, fiber);1367}1368function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber {1369 root.finishedWork = null;1370 root.finishedLanes = NoLanes;1371 const timeoutHandle = root.timeoutHandle;1372 if (timeoutHandle !== noTimeout) {1373 // The root previous suspended and scheduled a timeout to commit a fallback1374 // state. Now that we have additional work, cancel the timeout.1375 root.timeoutHandle = noTimeout;1376 // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above1377 cancelTimeout(timeoutHandle);1378 }1379 if (workInProgress !== null) {1380 let interruptedWork = workInProgress.return;1381 while (interruptedWork !== null) {1382 const current = interruptedWork.alternate;1383 unwindInterruptedWork(1384 current,1385 interruptedWork,1386 workInProgressRootRenderLanes,1387 );1388 interruptedWork = interruptedWork.return;1389 }1390 }1391 workInProgressRoot = root;1392 const rootWorkInProgress = createWorkInProgress(root.current, null);1393 workInProgress = rootWorkInProgress;1394 workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;1395 workInProgressRootExitStatus = RootInProgress;1396 workInProgressRootFatalError = null;1397 workInProgressRootSkippedLanes = NoLanes;1398 workInProgressRootInterleavedUpdatedLanes = NoLanes;1399 workInProgressRootRenderPhaseUpdatedLanes = NoLanes;1400 workInProgressRootPingedLanes = NoLanes;1401 workInProgressRootConcurrentErrors = null;1402 workInProgressRootRecoverableErrors = null;1403 enqueueInterleavedUpdates();1404 if (__DEV__) {1405 ReactStrictModeWarnings.discardPendingWarnings();1406 }1407 return rootWorkInProgress;1408}1409function handleError(root, thrownValue): void {1410 do {1411 let erroredWork = workInProgress;1412 try {1413 // Reset module-level state that was set during the render phase.1414 resetContextDependencies();1415 resetHooksAfterThrow();1416 resetCurrentDebugFiberInDEV();1417 // TODO: I found and added this missing line while investigating a1418 // separate issue. Write a regression test using string refs.1419 ReactCurrentOwner.current = null;1420 if (erroredWork === null || erroredWork.return === null) {1421 // Expected to be working on a non-root fiber. This is a fatal error1422 // because there's no ancestor that can handle it; the root is1423 // supposed to capture all errors that weren't caught by an error1424 // boundary.1425 workInProgressRootExitStatus = RootFatalErrored;1426 workInProgressRootFatalError = thrownValue;1427 // Set `workInProgress` to null. This represents advancing to the next1428 // sibling, or the parent if there are no siblings. But since the root1429 // has no siblings nor a parent, we set it to null. Usually this is1430 // handled by `completeUnitOfWork` or `unwindWork`, but since we're1431 // intentionally not calling those, we need set it here.1432 // TODO: Consider calling `unwindWork` to pop the contexts.1433 workInProgress = null;1434 return;1435 }1436 if (enableProfilerTimer && erroredWork.mode & ProfileMode) {1437 // Record the time spent rendering before an error was thrown. This1438 // avoids inaccurate Profiler durations in the case of a1439 // suspended render.1440 stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true);1441 }1442 if (enableSchedulingProfiler) {1443 markComponentRenderStopped();1444 if (1445 thrownValue !== null &&1446 typeof thrownValue === 'object' &&1447 typeof thrownValue.then === 'function'1448 ) {1449 const wakeable: Wakeable = (thrownValue: any);1450 markComponentSuspended(1451 erroredWork,1452 wakeable,1453 workInProgressRootRenderLanes,1454 );1455 } else {1456 markComponentErrored(1457 erroredWork,1458 thrownValue,1459 workInProgressRootRenderLanes,1460 );1461 }1462 }1463 throwException(1464 root,1465 erroredWork.return,1466 erroredWork,1467 thrownValue,1468 workInProgressRootRenderLanes,1469 );1470 completeUnitOfWork(erroredWork);1471 } catch (yetAnotherThrownValue) {1472 // Something in the return path also threw.1473 thrownValue = yetAnotherThrownValue;1474 if (workInProgress === erroredWork && erroredWork !== null) {1475 // If this boundary has already errored, then we had trouble processing1476 // the error. Bubble it to the next boundary.1477 erroredWork = erroredWork.return;1478 workInProgress = erroredWork;1479 } else {1480 erroredWork = workInProgress;1481 }1482 continue;1483 }1484 // Return to the normal work loop.1485 return;1486 } while (true);1487}1488function pushDispatcher() {1489 const prevDispatcher = ReactCurrentDispatcher.current;1490 ReactCurrentDispatcher.current = ContextOnlyDispatcher;1491 if (prevDispatcher === null) {1492 // The React isomorphic package does not include a default dispatcher.1493 // Instead the first renderer will lazily attach one, in order to give1494 // nicer error messages.1495 return ContextOnlyDispatcher;1496 } else {1497 return prevDispatcher;1498 }1499}1500function popDispatcher(prevDispatcher) {1501 ReactCurrentDispatcher.current = prevDispatcher;1502}1503export function markCommitTimeOfFallback() {1504 globalMostRecentFallbackTime = now();1505}1506export function markSkippedUpdateLanes(lane: Lane | Lanes): void {1507 workInProgressRootSkippedLanes = mergeLanes(1508 lane,1509 workInProgressRootSkippedLanes,1510 );1511}1512export function renderDidSuspend(): void {1513 if (workInProgressRootExitStatus === RootInProgress) {1514 workInProgressRootExitStatus = RootSuspended;1515 }1516}1517export function renderDidSuspendDelayIfPossible(): void {1518 if (1519 workInProgressRootExitStatus === RootInProgress ||1520 workInProgressRootExitStatus === RootSuspended ||1521 workInProgressRootExitStatus === RootErrored1522 ) {1523 workInProgressRootExitStatus = RootSuspendedWithDelay;1524 }1525 // Check if there are updates that we skipped tree that might have unblocked1526 // this render.1527 if (1528 workInProgressRoot !== null &&1529 (includesNonIdleWork(workInProgressRootSkippedLanes) ||1530 includesNonIdleWork(workInProgressRootInterleavedUpdatedLanes))1531 ) {1532 // Mark the current render as suspended so that we switch to working on1533 // the updates that were skipped. Usually we only suspend at the end of1534 // the render phase.1535 // TODO: We should probably always mark the root as suspended immediately1536 // (inside this function), since by suspending at the end of the render1537 // phase introduces a potential mistake where we suspend lanes that were1538 // pinged or updated while we were rendering.1539 markRootSuspended(workInProgressRoot, workInProgressRootRenderLanes);1540 }1541}1542export function renderDidError(error: mixed) {1543 if (workInProgressRootExitStatus !== RootSuspendedWithDelay) {1544 workInProgressRootExitStatus = RootErrored;1545 }1546 if (workInProgressRootConcurrentErrors === null) {1547 workInProgressRootConcurrentErrors = [error];1548 } else {1549 workInProgressRootConcurrentErrors.push(error);1550 }1551}1552// Called during render to determine if anything has suspended.1553// Returns false if we're not sure.1554export function renderHasNotSuspendedYet(): boolean {1555 // If something errored or completed, we can't really be sure,1556 // so those are false.1557 return workInProgressRootExitStatus === RootInProgress;1558}1559function renderRootSync(root: FiberRoot, lanes: Lanes) {1560 const prevExecutionContext = executionContext;1561 executionContext |= RenderContext;1562 const prevDispatcher = pushDispatcher();1563 // If the root or lanes have changed, throw out the existing stack1564 // and prepare a fresh one. Otherwise we'll continue where we left off.1565 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1566 if (enableUpdaterTracking) {1567 if (isDevToolsPresent) {1568 const memoizedUpdaters = root.memoizedUpdaters;1569 if (memoizedUpdaters.size > 0) {1570 restorePendingUpdaters(root, workInProgressRootRenderLanes);1571 memoizedUpdaters.clear();1572 }1573 // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.1574 // If we bailout on this work, we'll move them back (like above).1575 // It's important to move them now in case the work spawns more work at the same priority with different updaters.1576 // That way we can keep the current update and future updates separate.1577 movePendingFibersToMemoized(root, lanes);1578 }1579 }1580 workInProgressTransitions = getTransitionsForLanes(root, lanes);1581 prepareFreshStack(root, lanes);1582 }1583 if (__DEV__) {1584 if (enableDebugTracing) {1585 logRenderStarted(lanes);1586 }1587 }1588 if (enableSchedulingProfiler) {1589 markRenderStarted(lanes);1590 }1591 do {1592 try {1593 workLoopSync();1594 break;1595 } catch (thrownValue) {1596 handleError(root, thrownValue);1597 }1598 } while (true);1599 resetContextDependencies();1600 executionContext = prevExecutionContext;1601 popDispatcher(prevDispatcher);1602 if (workInProgress !== null) {1603 // This is a sync render, so we should have finished the whole tree.1604 throw new Error(1605 'Cannot commit an incomplete root. This error is likely caused by a ' +1606 'bug in React. Please file an issue.',1607 );1608 }1609 if (__DEV__) {1610 if (enableDebugTracing) {1611 logRenderStopped();1612 }1613 }1614 if (enableSchedulingProfiler) {1615 markRenderStopped();1616 }1617 // Set this to null to indicate there's no in-progress render.1618 workInProgressRoot = null;1619 workInProgressRootRenderLanes = NoLanes;1620 return workInProgressRootExitStatus;1621}1622// The work loop is an extremely hot path. Tell Closure not to inline it.1623/** @noinline */1624function workLoopSync() {1625 // Already timed out, so perform work without checking if we need to yield.1626 while (workInProgress !== null) {1627 performUnitOfWork(workInProgress);1628 }1629}1630function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {1631 const prevExecutionContext = executionContext;1632 executionContext |= RenderContext;1633 const prevDispatcher = pushDispatcher();1634 // If the root or lanes have changed, throw out the existing stack1635 // and prepare a fresh one. Otherwise we'll continue where we left off.1636 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1637 if (enableUpdaterTracking) {1638 if (isDevToolsPresent) {1639 const memoizedUpdaters = root.memoizedUpdaters;1640 if (memoizedUpdaters.size > 0) {1641 restorePendingUpdaters(root, workInProgressRootRenderLanes);1642 memoizedUpdaters.clear();1643 }1644 // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.1645 // If we bailout on this work, we'll move them back (like above).1646 // It's important to move them now in case the work spawns more work at the same priority with different updaters.1647 // That way we can keep the current update and future updates separate.1648 movePendingFibersToMemoized(root, lanes);1649 }1650 }1651 workInProgressTransitions = getTransitionsForLanes(root, lanes);1652 resetRenderTimer();1653 prepareFreshStack(root, lanes);1654 }1655 if (__DEV__) {1656 if (enableDebugTracing) {1657 logRenderStarted(lanes);1658 }1659 }1660 if (enableSchedulingProfiler) {1661 markRenderStarted(lanes);1662 }1663 do {1664 try {1665 workLoopConcurrent();1666 break;1667 } catch (thrownValue) {1668 handleError(root, thrownValue);1669 }1670 } while (true);1671 resetContextDependencies();1672 popDispatcher(prevDispatcher);1673 executionContext = prevExecutionContext;1674 if (__DEV__) {1675 if (enableDebugTracing) {1676 logRenderStopped();1677 }1678 }1679 // Check if the tree has completed.1680 if (workInProgress !== null) {1681 // Still work remaining.1682 if (enableSchedulingProfiler) {1683 markRenderYielded();1684 }1685 return RootInProgress;1686 } else {1687 // Completed the tree.1688 if (enableSchedulingProfiler) {1689 markRenderStopped();1690 }1691 // Set this to null to indicate there's no in-progress render.1692 workInProgressRoot = null;1693 workInProgressRootRenderLanes = NoLanes;1694 // Return the final exit status.1695 return workInProgressRootExitStatus;1696 }1697}1698/** @noinline */1699function workLoopConcurrent() {1700 // Perform work until Scheduler asks us to yield1701 while (workInProgress !== null && !shouldYield()) {1702 performUnitOfWork(workInProgress);1703 }1704}1705function performUnitOfWork(unitOfWork: Fiber): void {1706 // The current, flushed, state of this fiber is the alternate. Ideally1707 // nothing should rely on this, but relying on it here means that we don't1708 // need an additional field on the work in progress.1709 const current = unitOfWork.alternate;1710 setCurrentDebugFiberInDEV(unitOfWork);1711 let next;1712 if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {1713 startProfilerTimer(unitOfWork);1714 next = beginWork(current, unitOfWork, subtreeRenderLanes);1715 stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);1716 } else {1717 next = beginWork(current, unitOfWork, subtreeRenderLanes);1718 }1719 resetCurrentDebugFiberInDEV();1720 unitOfWork.memoizedProps = unitOfWork.pendingProps;1721 if (next === null) {1722 // If this doesn't spawn new work, complete the current work.1723 completeUnitOfWork(unitOfWork);1724 } else {1725 workInProgress = next;1726 }1727 ReactCurrentOwner.current = null;1728}1729function completeUnitOfWork(unitOfWork: Fiber): void {1730 // Attempt to complete the current unit of work, then move to the next1731 // sibling. If there are no more siblings, return to the parent fiber.1732 let completedWork = unitOfWork;1733 do {1734 // The current, flushed, state of this fiber is the alternate. Ideally1735 // nothing should rely on this, but relying on it here means that we don't1736 // need an additional field on the work in progress.1737 const current = completedWork.alternate;1738 const returnFiber = completedWork.return;1739 // Check if the work completed or if something threw.1740 if ((completedWork.flags & Incomplete) === NoFlags) {1741 setCurrentDebugFiberInDEV(completedWork);1742 let next;1743 if (1744 !enableProfilerTimer ||1745 (completedWork.mode & ProfileMode) === NoMode1746 ) {1747 next = completeWork(current, completedWork, subtreeRenderLanes);1748 } else {1749 startProfilerTimer(completedWork);1750 next = completeWork(current, completedWork, subtreeRenderLanes);1751 // Update render duration assuming we didn't error.1752 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1753 }1754 resetCurrentDebugFiberInDEV();1755 if (next !== null) {1756 // Completing this fiber spawned new work. Work on that next.1757 workInProgress = next;1758 return;1759 }1760 } else {1761 // This fiber did not complete because something threw. Pop values off1762 // the stack without entering the complete phase. If this is a boundary,1763 // capture values if possible.1764 const next = unwindWork(current, completedWork, subtreeRenderLanes);1765 // Because this fiber did not complete, don't reset its lanes.1766 if (next !== null) {1767 // If completing this work spawned new work, do that next. We'll come1768 // back here again.1769 // Since we're restarting, remove anything that is not a host effect1770 // from the effect tag.1771 next.flags &= HostEffectMask;1772 workInProgress = next;1773 return;1774 }1775 if (1776 enableProfilerTimer &&1777 (completedWork.mode & ProfileMode) !== NoMode1778 ) {1779 // Record the render duration for the fiber that errored.1780 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1781 // Include the time spent working on failed children before continuing.1782 let actualDuration = completedWork.actualDuration;1783 let child = completedWork.child;1784 while (child !== null) {1785 actualDuration += child.actualDuration;1786 child = child.sibling;1787 }1788 completedWork.actualDuration = actualDuration;1789 }1790 if (returnFiber !== null) {1791 // Mark the parent fiber as incomplete and clear its subtree flags.1792 returnFiber.flags |= Incomplete;1793 returnFiber.subtreeFlags = NoFlags;1794 returnFiber.deletions = null;1795 } else {1796 // We've unwound all the way to the root.1797 workInProgressRootExitStatus = RootDidNotComplete;1798 workInProgress = null;1799 return;1800 }1801 }1802 const siblingFiber = completedWork.sibling;1803 if (siblingFiber !== null) {1804 // If there is more work to do in this returnFiber, do that next.1805 workInProgress = siblingFiber;1806 return;1807 }1808 // Otherwise, return to the parent1809 completedWork = returnFiber;1810 // Update the next thing we're working on in case something throws.1811 workInProgress = completedWork;1812 } while (completedWork !== null);1813 // We've reached the root.1814 if (workInProgressRootExitStatus === RootInProgress) {1815 workInProgressRootExitStatus = RootCompleted;1816 }1817}1818function commitRoot(1819 root: FiberRoot,1820 recoverableErrors: null | Array<mixed>,1821 transitions: Array<Transition> | null,1822) {1823 // TODO: This no longer makes any sense. We already wrap the mutation and1824 // layout phases. Should be able to remove.1825 const previousUpdateLanePriority = getCurrentUpdatePriority();1826 const prevTransition = ReactCurrentBatchConfig.transition;1827 try {1828 ReactCurrentBatchConfig.transition = null;1829 setCurrentUpdatePriority(DiscreteEventPriority);1830 commitRootImpl(1831 root,1832 recoverableErrors,1833 transitions,1834 previousUpdateLanePriority,1835 );1836 } finally {1837 ReactCurrentBatchConfig.transition = prevTransition;1838 setCurrentUpdatePriority(previousUpdateLanePriority);1839 }1840 return null;1841}1842function commitRootImpl(1843 root: FiberRoot,1844 recoverableErrors: null | Array<mixed>,1845 transitions: Array<Transition> | null,1846 renderPriorityLevel: EventPriority,1847) {1848 do {1849 // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which1850 // means `flushPassiveEffects` will sometimes result in additional1851 // passive effects. So we need to keep flushing in a loop until there are1852 // no more pending effects.1853 // TODO: Might be better if `flushPassiveEffects` did not automatically1854 // flush synchronous work at the end, to avoid factoring hazards like this.1855 flushPassiveEffects();1856 } while (rootWithPendingPassiveEffects !== null);1857 flushRenderPhaseStrictModeWarningsInDEV();1858 if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {1859 throw new Error('Should not already be working.');1860 }1861 const finishedWork = root.finishedWork;1862 const lanes = root.finishedLanes;1863 if (__DEV__) {1864 if (enableDebugTracing) {1865 logCommitStarted(lanes);1866 }1867 }1868 if (enableSchedulingProfiler) {1869 markCommitStarted(lanes);1870 }1871 if (finishedWork === null) {1872 if (__DEV__) {1873 if (enableDebugTracing) {1874 logCommitStopped();1875 }1876 }1877 if (enableSchedulingProfiler) {1878 markCommitStopped();1879 }1880 return null;1881 } else {1882 if (__DEV__) {1883 if (lanes === NoLanes) {1884 console.error(1885 'root.finishedLanes should not be empty during a commit. This is a ' +1886 'bug in React.',1887 );1888 }1889 }1890 }1891 root.finishedWork = null;1892 root.finishedLanes = NoLanes;1893 if (finishedWork === root.current) {1894 throw new Error(1895 'Cannot commit the same tree as before. This error is likely caused by ' +1896 'a bug in React. Please file an issue.',1897 );1898 }1899 // commitRoot never returns a continuation; it always finishes synchronously.1900 // So we can clear these now to allow a new callback to be scheduled.1901 root.callbackNode = null;1902 root.callbackPriority = NoLane;1903 // Update the first and last pending times on this root. The new first1904 // pending time is whatever is left on the root fiber.1905 let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);1906 markRootFinished(root, remainingLanes);1907 if (root === workInProgressRoot) {1908 // We can reset these now that they are finished.1909 workInProgressRoot = null;1910 workInProgress = null;1911 workInProgressRootRenderLanes = NoLanes;1912 } else {1913 // This indicates that the last root we worked on is not the same one that1914 // we're committing now. This most commonly happens when a suspended root1915 // times out.1916 }1917 // If there are pending passive effects, schedule a callback to process them.1918 // Do this as early as possible, so it is queued before anything else that1919 // might get scheduled in the commit phase. (See #16714.)1920 // TODO: Delete all other places that schedule the passive effect callback1921 // They're redundant.1922 if (1923 (finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||1924 (finishedWork.flags & PassiveMask) !== NoFlags1925 ) {1926 if (!rootDoesHavePassiveEffects) {1927 rootDoesHavePassiveEffects = true;1928 pendingPassiveEffectsRemainingLanes = remainingLanes;1929 // workInProgressTransitions might be overwritten, so we want1930 // to store it in pendingPassiveTransitions until they get processed1931 // We need to pass this through as an argument to commitRoot1932 // because workInProgressTransitions might have changed between1933 // the previous render and commit if we throttle the commit1934 // with setTimeout1935 pendingPassiveTransitions = transitions;1936 scheduleCallback(NormalSchedulerPriority, () => {1937 flushPassiveEffects();1938 // This render triggered passive effects: release the root cache pool1939 // *after* passive effects fire to avoid freeing a cache pool that may1940 // be referenced by a node in the tree (HostRoot, Cache boundary etc)1941 return null;1942 });1943 }1944 }1945 // Check if there are any effects in the whole tree.1946 // TODO: This is left over from the effect list implementation, where we had1947 // to check for the existence of `firstEffect` to satisfy Flow. I think the1948 // only other reason this optimization exists is because it affects profiling.1949 // Reconsider whether this is necessary.1950 const subtreeHasEffects =1951 (finishedWork.subtreeFlags &1952 (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1953 NoFlags;1954 const rootHasEffect =1955 (finishedWork.flags &1956 (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1957 NoFlags;1958 if (subtreeHasEffects || rootHasEffect) {1959 const prevTransition = ReactCurrentBatchConfig.transition;1960 ReactCurrentBatchConfig.transition = null;1961 const previousPriority = getCurrentUpdatePriority();1962 setCurrentUpdatePriority(DiscreteEventPriority);1963 const prevExecutionContext = executionContext;1964 executionContext |= CommitContext;1965 // Reset this to null before calling lifecycles1966 ReactCurrentOwner.current = null;1967 // The commit phase is broken into several sub-phases. We do a separate pass1968 // of the effect list for each phase: all mutation effects come before all1969 // layout effects, and so on.1970 // The first phase a "before mutation" phase. We use this phase to read the1971 // state of the host tree right before we mutate it. This is where1972 // getSnapshotBeforeUpdate is called.1973 const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(1974 root,1975 finishedWork,1976 );1977 if (enableProfilerTimer) {1978 // Mark the current commit time to be shared by all Profilers in this1979 // batch. This enables them to be grouped later.1980 recordCommitTime();1981 }1982 if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {1983 // Track the root here, rather than in commitLayoutEffects(), because of ref setters.1984 // Updates scheduled during ref detachment should also be flagged.1985 rootCommittingMutationOrLayoutEffects = root;1986 }1987 // The next phase is the mutation phase, where we mutate the host tree.1988 commitMutationEffects(root, finishedWork, lanes);1989 if (enableCreateEventHandleAPI) {1990 if (shouldFireAfterActiveInstanceBlur) {1991 afterActiveInstanceBlur();1992 }1993 }1994 resetAfterCommit(root.containerInfo);1995 // The work-in-progress tree is now the current tree. This must come after1996 // the mutation phase, so that the previous tree is still current during1997 // componentWillUnmount, but before the layout phase, so that the finished1998 // work is current during componentDidMount/Update.1999 root.current = finishedWork;2000 // The next phase is the layout phase, where we call effects that read2001 // the host tree after it's been mutated. The idiomatic use case for this is2002 // layout, but class component lifecycles also fire here for legacy reasons.2003 if (__DEV__) {2004 if (enableDebugTracing) {2005 logLayoutEffectsStarted(lanes);2006 }2007 }2008 if (enableSchedulingProfiler) {2009 markLayoutEffectsStarted(lanes);2010 }2011 commitLayoutEffects(finishedWork, root, lanes);2012 if (__DEV__) {2013 if (enableDebugTracing) {2014 logLayoutEffectsStopped();2015 }2016 }2017 if (enableSchedulingProfiler) {2018 markLayoutEffectsStopped();2019 }2020 if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {2021 rootCommittingMutationOrLayoutEffects = null;2022 }2023 // Tell Scheduler to yield at the end of the frame, so the browser has an2024 // opportunity to paint.2025 requestPaint();2026 executionContext = prevExecutionContext;2027 // Reset the priority to the previous non-sync value.2028 setCurrentUpdatePriority(previousPriority);2029 ReactCurrentBatchConfig.transition = prevTransition;2030 } else {2031 // No effects.2032 root.current = finishedWork;2033 // Measure these anyway so the flamegraph explicitly shows that there were2034 // no effects.2035 // TODO: Maybe there's a better way to report this.2036 if (enableProfilerTimer) {2037 recordCommitTime();2038 }2039 }2040 const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;2041 if (rootDoesHavePassiveEffects) {2042 // This commit has passive effects. Stash a reference to them. But don't2043 // schedule a callback until after flushing layout work.2044 rootDoesHavePassiveEffects = false;2045 rootWithPendingPassiveEffects = root;2046 pendingPassiveEffectsLanes = lanes;2047 } else {2048 // There were no passive effects, so we can immediately release the cache2049 // pool for this render.2050 releaseRootPooledCache(root, remainingLanes);2051 if (__DEV__) {2052 nestedPassiveUpdateCount = 0;2053 rootWithPassiveNestedUpdates = null;2054 }2055 }2056 // Read this again, since an effect might have updated it2057 remainingLanes = root.pendingLanes;2058 // Check if there's remaining work on this root2059 // TODO: This is part of the `componentDidCatch` implementation. Its purpose2060 // is to detect whether something might have called setState inside2061 // `componentDidCatch`. The mechanism is known to be flawed because `setState`2062 // inside `componentDidCatch` is itself flawed â that's why we recommend2063 // `getDerivedStateFromError` instead. However, it could be improved by2064 // checking if remainingLanes includes Sync work, instead of whether there's2065 // any work remaining at all (which would also include stuff like Suspense2066 // retries or transitions). It's been like this for a while, though, so fixing2067 // it probably isn't that urgent.2068 if (remainingLanes === NoLanes) {2069 // If there's no remaining work, we can clear the set of already failed2070 // error boundaries.2071 legacyErrorBoundariesThatAlreadyFailed = null;2072 }2073 if (__DEV__ && enableStrictEffects) {2074 if (!rootDidHavePassiveEffects) {2075 commitDoubleInvokeEffectsInDEV(root.current, false);2076 }2077 }2078 onCommitRootDevTools(finishedWork.stateNode, renderPriorityLevel);2079 if (enableUpdaterTracking) {2080 if (isDevToolsPresent) {2081 root.memoizedUpdaters.clear();2082 }2083 }2084 if (__DEV__) {2085 onCommitRootTestSelector();2086 }2087 // Always call this before exiting `commitRoot`, to ensure that any2088 // additional work on this root is scheduled.2089 ensureRootIsScheduled(root, now());2090 if (recoverableErrors !== null) {2091 // There were errors during this render, but recovered from them without2092 // needing to surface it to the UI. We log them here.2093 const onRecoverableError = root.onRecoverableError;2094 for (let i = 0; i < recoverableErrors.length; i++) {2095 const recoverableError = recoverableErrors[i];2096 onRecoverableError(recoverableError);2097 }2098 }2099 if (hasUncaughtError) {2100 hasUncaughtError = false;2101 const error = firstUncaughtError;2102 firstUncaughtError = null;2103 throw error;2104 }2105 // If the passive effects are the result of a discrete render, flush them2106 // synchronously at the end of the current task so that the result is2107 // immediately observable. Otherwise, we assume that they are not2108 // order-dependent and do not need to be observed by external systems, so we2109 // can wait until after paint.2110 // TODO: We can optimize this by not scheduling the callback earlier. Since we2111 // currently schedule the callback in multiple places, will wait until those2112 // are consolidated.2113 if (2114 includesSomeLane(pendingPassiveEffectsLanes, SyncLane) &&2115 root.tag !== LegacyRoot2116 ) {2117 flushPassiveEffects();2118 }2119 // Read this again, since a passive effect might have updated it2120 remainingLanes = root.pendingLanes;2121 if (includesSomeLane(remainingLanes, (SyncLane: Lane))) {2122 if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {2123 markNestedUpdateScheduled();2124 }2125 // Count the number of times the root synchronously re-renders without2126 // finishing. If there are too many, it indicates an infinite update loop.2127 if (root === rootWithNestedUpdates) {2128 nestedUpdateCount++;2129 } else {2130 nestedUpdateCount = 0;2131 rootWithNestedUpdates = root;2132 }2133 } else {2134 nestedUpdateCount = 0;2135 }2136 // If layout work was scheduled, flush it now.2137 flushSyncCallbacks();2138 if (__DEV__) {2139 if (enableDebugTracing) {2140 logCommitStopped();2141 }2142 }2143 if (enableSchedulingProfiler) {2144 markCommitStopped();2145 }2146 return null;2147}2148function releaseRootPooledCache(root: FiberRoot, remainingLanes: Lanes) {2149 if (enableCache) {2150 const pooledCacheLanes = (root.pooledCacheLanes &= remainingLanes);2151 if (pooledCacheLanes === NoLanes) {2152 // None of the remaining work relies on the cache pool. Clear it so2153 // subsequent requests get a new cache2154 const pooledCache = root.pooledCache;2155 if (pooledCache != null) {2156 root.pooledCache = null;2157 releaseCache(pooledCache);2158 }2159 }2160 }2161}2162export function flushPassiveEffects(): boolean {2163 // Returns whether passive effects were flushed.2164 // TODO: Combine this check with the one in flushPassiveEFfectsImpl. We should2165 // probably just combine the two functions. I believe they were only separate2166 // in the first place because we used to wrap it with2167 // `Scheduler.runWithPriority`, which accepts a function. But now we track the2168 // priority within React itself, so we can mutate the variable directly.2169 if (rootWithPendingPassiveEffects !== null) {2170 // Cache the root since rootWithPendingPassiveEffects is cleared in2171 // flushPassiveEffectsImpl2172 const root = rootWithPendingPassiveEffects;2173 // Cache and clear the remaining lanes flag; it must be reset since this2174 // method can be called from various places, not always from commitRoot2175 // where the remaining lanes are known2176 const remainingLanes = pendingPassiveEffectsRemainingLanes;2177 pendingPassiveEffectsRemainingLanes = NoLanes;2178 const renderPriority = lanesToEventPriority(pendingPassiveEffectsLanes);2179 const priority = lowerEventPriority(DefaultEventPriority, renderPriority);2180 const prevTransition = ReactCurrentBatchConfig.transition;2181 const previousPriority = getCurrentUpdatePriority();2182 try {2183 ReactCurrentBatchConfig.transition = null;2184 setCurrentUpdatePriority(priority);2185 return flushPassiveEffectsImpl();2186 } finally {2187 setCurrentUpdatePriority(previousPriority);2188 ReactCurrentBatchConfig.transition = prevTransition;2189 // Once passive effects have run for the tree - giving components a2190 // chance to retain cache instances they use - release the pooled2191 // cache at the root (if there is one)2192 releaseRootPooledCache(root, remainingLanes);2193 }2194 }2195 return false;2196}2197export function enqueuePendingPassiveProfilerEffect(fiber: Fiber): void {2198 if (enableProfilerTimer && enableProfilerCommitHooks) {2199 pendingPassiveProfilerEffects.push(fiber);2200 if (!rootDoesHavePassiveEffects) {2201 rootDoesHavePassiveEffects = true;2202 scheduleCallback(NormalSchedulerPriority, () => {2203 flushPassiveEffects();2204 return null;2205 });2206 }2207 }2208}2209function flushPassiveEffectsImpl() {2210 if (rootWithPendingPassiveEffects === null) {2211 return false;2212 }2213 // Cache and clear the transitions flag2214 const transitions = pendingPassiveTransitions;2215 pendingPassiveTransitions = null;2216 const root = rootWithPendingPassiveEffects;2217 const lanes = pendingPassiveEffectsLanes;2218 rootWithPendingPassiveEffects = null;2219 // TODO: This is sometimes out of sync with rootWithPendingPassiveEffects.2220 // Figure out why and fix it. It's not causing any known issues (probably2221 // because it's only used for profiling), but it's a refactor hazard.2222 pendingPassiveEffectsLanes = NoLanes;2223 if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {2224 throw new Error('Cannot flush passive effects while already rendering.');2225 }2226 if (__DEV__) {2227 isFlushingPassiveEffects = true;2228 didScheduleUpdateDuringPassiveEffects = false;2229 if (enableDebugTracing) {2230 logPassiveEffectsStarted(lanes);2231 }2232 }2233 if (enableSchedulingProfiler) {2234 markPassiveEffectsStarted(lanes);2235 }2236 const prevExecutionContext = executionContext;2237 executionContext |= CommitContext;2238 commitPassiveUnmountEffects(root.current);2239 commitPassiveMountEffects(root, root.current, lanes, transitions);2240 // TODO: Move to commitPassiveMountEffects2241 if (enableProfilerTimer && enableProfilerCommitHooks) {2242 const profilerEffects = pendingPassiveProfilerEffects;2243 pendingPassiveProfilerEffects = [];2244 for (let i = 0; i < profilerEffects.length; i++) {2245 const fiber = ((profilerEffects[i]: any): Fiber);2246 commitPassiveEffectDurations(root, fiber);2247 }2248 }2249 if (__DEV__) {2250 if (enableDebugTracing) {2251 logPassiveEffectsStopped();2252 }2253 }2254 if (enableSchedulingProfiler) {2255 markPassiveEffectsStopped();2256 }2257 if (__DEV__ && enableStrictEffects) {2258 commitDoubleInvokeEffectsInDEV(root.current, true);2259 }2260 executionContext = prevExecutionContext;2261 flushSyncCallbacks();2262 if (enableTransitionTracing) {2263 const prevPendingTransitionCallbacks = currentPendingTransitionCallbacks;2264 const prevRootTransitionCallbacks = root.transitionCallbacks;2265 if (2266 prevPendingTransitionCallbacks !== null &&2267 prevRootTransitionCallbacks !== null2268 ) {2269 // TODO(luna) Refactor this code into the Host Config2270 // TODO(luna) The end time here is not necessarily accurate2271 // because passive effects could be called before paint2272 // (synchronously) or after paint (normally). We need2273 // to come up with a way to get the correct end time for both cases.2274 // One solution is in the host config, if the passive effects2275 // have not yet been run, make a call to flush the passive effects2276 // right after paint.2277 const endTime = now();2278 currentPendingTransitionCallbacks = null;2279 scheduleCallback(IdleSchedulerPriority, () =>2280 processTransitionCallbacks(2281 prevPendingTransitionCallbacks,2282 endTime,2283 prevRootTransitionCallbacks,2284 ),2285 );2286 }2287 }2288 if (__DEV__) {2289 // If additional passive effects were scheduled, increment a counter. If this2290 // exceeds the limit, we'll fire a warning.2291 if (didScheduleUpdateDuringPassiveEffects) {2292 if (root === rootWithPassiveNestedUpdates) {2293 nestedPassiveUpdateCount++;2294 } else {2295 nestedPassiveUpdateCount = 0;2296 rootWithPassiveNestedUpdates = root;2297 }2298 } else {2299 nestedPassiveUpdateCount = 0;2300 }2301 isFlushingPassiveEffects = false;2302 didScheduleUpdateDuringPassiveEffects = false;2303 }2304 // TODO: Move to commitPassiveMountEffects2305 onPostCommitRootDevTools(root);2306 if (enableProfilerTimer && enableProfilerCommitHooks) {2307 const stateNode = root.current.stateNode;2308 stateNode.effectDuration = 0;2309 stateNode.passiveEffectDuration = 0;2310 }2311 return true;2312}2313export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {2314 return (2315 legacyErrorBoundariesThatAlreadyFailed !== null &&2316 legacyErrorBoundariesThatAlreadyFailed.has(instance)2317 );2318}2319export function markLegacyErrorBoundaryAsFailed(instance: mixed) {2320 if (legacyErrorBoundariesThatAlreadyFailed === null) {2321 legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);2322 } else {2323 legacyErrorBoundariesThatAlreadyFailed.add(instance);2324 }2325}2326function prepareToThrowUncaughtError(error: mixed) {2327 if (!hasUncaughtError) {2328 hasUncaughtError = true;2329 firstUncaughtError = error;2330 }2331}2332export const onUncaughtError = prepareToThrowUncaughtError;2333function captureCommitPhaseErrorOnRoot(2334 rootFiber: Fiber,2335 sourceFiber: Fiber,2336 error: mixed,2337) {2338 const errorInfo = createCapturedValue(error, sourceFiber);2339 const update = createRootErrorUpdate(rootFiber, errorInfo, (SyncLane: Lane));2340 enqueueUpdate(rootFiber, update, (SyncLane: Lane));2341 const eventTime = requestEventTime();2342 const root = markUpdateLaneFromFiberToRoot(rootFiber, (SyncLane: Lane));2343 if (root !== null) {2344 markRootUpdated(root, SyncLane, eventTime);2345 ensureRootIsScheduled(root, eventTime);2346 }2347}2348export function captureCommitPhaseError(2349 sourceFiber: Fiber,2350 nearestMountedAncestor: Fiber | null,2351 error: mixed,2352) {2353 if (__DEV__) {2354 reportUncaughtErrorInDEV(error);2355 setIsRunningInsertionEffect(false);2356 }2357 if (sourceFiber.tag === HostRoot) {2358 // Error was thrown at the root. There is no parent, so the root2359 // itself should capture it.2360 captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);2361 return;2362 }2363 let fiber = null;2364 if (skipUnmountedBoundaries) {2365 fiber = nearestMountedAncestor;2366 } else {2367 fiber = sourceFiber.return;2368 }2369 while (fiber !== null) {2370 if (fiber.tag === HostRoot) {2371 captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);2372 return;2373 } else if (fiber.tag === ClassComponent) {2374 const ctor = fiber.type;2375 const instance = fiber.stateNode;2376 if (2377 typeof ctor.getDerivedStateFromError === 'function' ||2378 (typeof instance.componentDidCatch === 'function' &&2379 !isAlreadyFailedLegacyErrorBoundary(instance))2380 ) {2381 const errorInfo = createCapturedValue(error, sourceFiber);2382 const update = createClassErrorUpdate(2383 fiber,2384 errorInfo,2385 (SyncLane: Lane),2386 );2387 enqueueUpdate(fiber, update, (SyncLane: Lane));2388 const eventTime = requestEventTime();2389 const root = markUpdateLaneFromFiberToRoot(fiber, (SyncLane: Lane));2390 if (root !== null) {2391 markRootUpdated(root, SyncLane, eventTime);2392 ensureRootIsScheduled(root, eventTime);2393 }2394 return;2395 }2396 }2397 fiber = fiber.return;2398 }2399 if (__DEV__) {2400 // TODO: Until we re-land skipUnmountedBoundaries (see #20147), this warning2401 // will fire for errors that are thrown by destroy functions inside deleted2402 // trees. What it should instead do is propagate the error to the parent of2403 // the deleted tree. In the meantime, do not add this warning to the2404 // allowlist; this is only for our internal use.2405 console.error(2406 'Internal React error: Attempted to capture a commit phase error ' +2407 'inside a detached tree. This indicates a bug in React. Likely ' +2408 'causes include deleting the same fiber more than once, committing an ' +2409 'already-finished tree, or an inconsistent return pointer.\n\n' +2410 'Error message:\n\n%s',2411 error,2412 );2413 }2414}2415export function pingSuspendedRoot(2416 root: FiberRoot,2417 wakeable: Wakeable,2418 pingedLanes: Lanes,2419) {2420 const pingCache = root.pingCache;2421 if (pingCache !== null) {2422 // The wakeable resolved, so we no longer need to memoize, because it will2423 // never be thrown again.2424 pingCache.delete(wakeable);2425 }2426 const eventTime = requestEventTime();2427 markRootPinged(root, pingedLanes, eventTime);2428 warnIfSuspenseResolutionNotWrappedWithActDEV(root);2429 if (2430 workInProgressRoot === root &&2431 isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)2432 ) {2433 // Received a ping at the same priority level at which we're currently2434 // rendering. We might want to restart this render. This should mirror2435 // the logic of whether or not a root suspends once it completes.2436 // TODO: If we're rendering sync either due to Sync, Batched or expired,2437 // we should probably never restart.2438 // If we're suspended with delay, or if it's a retry, we'll always suspend2439 // so we can always restart.2440 if (2441 workInProgressRootExitStatus === RootSuspendedWithDelay ||2442 (workInProgressRootExitStatus === RootSuspended &&2443 includesOnlyRetries(workInProgressRootRenderLanes) &&2444 now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS)2445 ) {2446 // Restart from the root.2447 prepareFreshStack(root, NoLanes);2448 } else {2449 // Even though we can't restart right now, we might get an2450 // opportunity later. So we mark this render as having a ping.2451 workInProgressRootPingedLanes = mergeLanes(2452 workInProgressRootPingedLanes,2453 pingedLanes,2454 );2455 }2456 }2457 ensureRootIsScheduled(root, eventTime);2458}2459function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {2460 // The boundary fiber (a Suspense component or SuspenseList component)2461 // previously was rendered in its fallback state. One of the promises that2462 // suspended it has resolved, which means at least part of the tree was2463 // likely unblocked. Try rendering again, at a new lanes.2464 if (retryLane === NoLane) {2465 // TODO: Assign this to `suspenseState.retryLane`? to avoid2466 // unnecessary entanglement?2467 retryLane = requestRetryLane(boundaryFiber);2468 }2469 // TODO: Special case idle priority?2470 const eventTime = requestEventTime();2471 const root = markUpdateLaneFromFiberToRoot(boundaryFiber, retryLane);2472 if (root !== null) {2473 markRootUpdated(root, retryLane, eventTime);2474 ensureRootIsScheduled(root, eventTime);2475 }2476}2477export function retryDehydratedSuspenseBoundary(boundaryFiber: Fiber) {2478 const suspenseState: null | SuspenseState = boundaryFiber.memoizedState;2479 let retryLane = NoLane;2480 if (suspenseState !== null) {2481 retryLane = suspenseState.retryLane;2482 }2483 retryTimedOutBoundary(boundaryFiber, retryLane);2484}2485export function resolveRetryWakeable(boundaryFiber: Fiber, wakeable: Wakeable) {2486 let retryLane = NoLane; // Default2487 let retryCache: WeakSet<Wakeable> | Set<Wakeable> | null;...
ReactFiberWorkLoop.new.js
Source:ReactFiberWorkLoop.new.js
...473 if (root === null) {474 return null;475 }476 // Mark that the root has a pending update.477 markRootUpdated(root, lane, eventTime);478 if (479 (executionContext & RenderContext) !== NoLanes &&480 root === workInProgressRoot481 ) {482 // This update was dispatched during the render phase. This is a mistake483 // if the update originates from user space (with the exception of local484 // hook updates, which are handled differently and don't reach this485 // function), but there are some internal React features that use this as486 // an implementation detail, like selective hydration.487 warnAboutRenderPhaseUpdatesInDEV(fiber);488 // Track lanes that were updated during the render phase489 workInProgressRootRenderPhaseUpdatedLanes = mergeLanes(490 workInProgressRootRenderPhaseUpdatedLanes,491 lane,492 );493 } else {494 // This is a normal update, scheduled from outside the render phase. For495 // example, during an input event.496 if (enableUpdaterTracking) {497 if (isDevToolsPresent) {498 addFiberToLanesMap(root, fiber, lane);499 }500 }501 warnIfUpdatesNotWrappedWithActDEV(fiber);502 if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {503 if (504 (executionContext & CommitContext) !== NoContext &&505 root === rootCommittingMutationOrLayoutEffects506 ) {507 if (fiber.mode & ProfileMode) {508 let current = fiber;509 while (current !== null) {510 if (current.tag === Profiler) {511 const {id, onNestedUpdateScheduled} = current.memoizedProps;512 if (typeof onNestedUpdateScheduled === 'function') {513 onNestedUpdateScheduled(id);514 }515 }516 current = current.return;517 }518 }519 }520 }521 if (enableTransitionTracing) {522 const transition = ReactCurrentBatchConfig.transition;523 if (transition !== null) {524 if (transition.startTime === -1) {525 transition.startTime = now();526 }527 addTransitionToLanesMap(root, transition, lane);528 }529 }530 if (root.isDehydrated && root.tag !== LegacyRoot) {531 // This root's shell hasn't hydrated yet. Revert to client rendering.532 if (workInProgressRoot === root) {533 // If this happened during an interleaved event, interrupt the534 // in-progress hydration. Theoretically, we could attempt to force a535 // synchronous hydration before switching to client rendering, but the536 // most common reason the shell hasn't hydrated yet is because it537 // suspended. So it's very likely to suspend again anyway. For538 // simplicity, we'll skip that atttempt and go straight to539 // client rendering.540 //541 // Another way to model this would be to give the initial hydration its542 // own special lane. However, it may not be worth adding a lane solely543 // for this purpose, so we'll wait until we find another use case before544 // adding it.545 //546 // TODO: Consider only interrupting hydration if the priority of the547 // update is higher than default.548 prepareFreshStack(root, NoLanes);549 }550 root.isDehydrated = false;551 const error = new Error(552 'This root received an early update, before anything was able ' +553 'hydrate. Switched the entire root to client rendering.',554 );555 const onRecoverableError = root.onRecoverableError;556 onRecoverableError(error);557 } else if (root === workInProgressRoot) {558 // TODO: Consolidate with `isInterleavedUpdate` check559 // Received an update to a tree that's in the middle of rendering. Mark560 // that there was an interleaved update work on this root. Unless the561 // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render562 // phase update. In that case, we don't treat render phase updates as if563 // they were interleaved, for backwards compat reasons.564 if (565 deferRenderPhaseUpdateToNextBatch ||566 (executionContext & RenderContext) === NoContext567 ) {568 workInProgressRootInterleavedUpdatedLanes = mergeLanes(569 workInProgressRootInterleavedUpdatedLanes,570 lane,571 );572 }573 if (workInProgressRootExitStatus === RootSuspendedWithDelay) {574 // The root already suspended with a delay, which means this render575 // definitely won't finish. Since we have a new update, let's mark it as576 // suspended now, right before marking the incoming update. This has the577 // effect of interrupting the current render and switching to the update.578 // TODO: Make sure this doesn't override pings that happen while we've579 // already started rendering.580 markRootSuspended(root, workInProgressRootRenderLanes);581 }582 }583 ensureRootIsScheduled(root, eventTime);584 if (585 lane === SyncLane &&586 executionContext === NoContext &&587 (fiber.mode & ConcurrentMode) === NoMode &&588 // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.589 !(__DEV__ && ReactCurrentActQueue.isBatchingLegacy)590 ) {591 // Flush the synchronous work now, unless we're already working or inside592 // a batch. This is intentionally inside scheduleUpdateOnFiber instead of593 // scheduleCallbackForFiber to preserve the ability to schedule a callback594 // without immediately flushing it. We only do this for user-initiated595 // updates, to preserve historical behavior of legacy mode.596 resetRenderTimer();597 flushSyncCallbacksOnlyInLegacyMode();598 }599 }600 return root;601}602export function scheduleInitialHydrationOnRoot(603 root: FiberRoot,604 lane: Lane,605 eventTime: number,606) {607 // This is a special fork of scheduleUpdateOnFiber that is only used to608 // schedule the initial hydration of a root that has just been created. Most609 // of the stuff in scheduleUpdateOnFiber can be skipped.610 //611 // The main reason for this separate path, though, is to distinguish the612 // initial children from subsequent updates. In fully client-rendered roots613 // (createRoot instead of hydrateRoot), all top-level renders are modeled as614 // updates, but hydration roots are special because the initial render must615 // match what was rendered on the server.616 const current = root.current;617 current.lanes = lane;618 markRootUpdated(root, lane, eventTime);619 ensureRootIsScheduled(root, eventTime);620}621// This is split into a separate function so we can mark a fiber with pending622// work without treating it as a typical update that originates from an event;623// e.g. retrying a Suspense boundary isn't an update, but it does schedule work624// on a fiber.625function markUpdateLaneFromFiberToRoot(626 sourceFiber: Fiber,627 lane: Lane,628): FiberRoot | null {629 // Update the source fiber's lanes630 sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);631 let alternate = sourceFiber.alternate;632 if (alternate !== null) {633 alternate.lanes = mergeLanes(alternate.lanes, lane);634 }635 if (__DEV__) {636 if (637 alternate === null &&638 (sourceFiber.flags & (Placement | Hydrating)) !== NoFlags639 ) {640 warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);641 }642 }643 // Walk the parent path to the root and update the child lanes.644 let node = sourceFiber;645 let parent = sourceFiber.return;646 while (parent !== null) {647 parent.childLanes = mergeLanes(parent.childLanes, lane);648 alternate = parent.alternate;649 if (alternate !== null) {650 alternate.childLanes = mergeLanes(alternate.childLanes, lane);651 } else {652 if (__DEV__) {653 if ((parent.flags & (Placement | Hydrating)) !== NoFlags) {654 warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);655 }656 }657 }658 node = parent;659 parent = parent.return;660 }661 if (node.tag === HostRoot) {662 const root: FiberRoot = node.stateNode;663 return root;664 } else {665 return null;666 }667}668export function isInterleavedUpdate(fiber: Fiber, lane: Lane) {669 return (670 // TODO: Optimize slightly by comparing to root that fiber belongs to.671 // Requires some refactoring. Not a big deal though since it's rare for672 // concurrent apps to have more than a single root.673 workInProgressRoot !== null &&674 (fiber.mode & ConcurrentMode) !== NoMode &&675 // If this is a render phase update (i.e. UNSAFE_componentWillReceiveProps),676 // then don't treat this as an interleaved update. This pattern is677 // accompanied by a warning but we haven't fully deprecated it yet. We can678 // remove once the deferRenderPhaseUpdateToNextBatch flag is enabled.679 (deferRenderPhaseUpdateToNextBatch ||680 (executionContext & RenderContext) === NoContext)681 );682}683// Use this function to schedule a task for a root. There's only one task per684// root; if a task was already scheduled, we'll check to make sure the priority685// of the existing task is the same as the priority of the next level that the686// root has work on. This function is called on every update, and right before687// exiting a task.688function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {689 const existingCallbackNode = root.callbackNode;690 // Check if any lanes are being starved by other work. If so, mark them as691 // expired so we know to work on those next.692 markStarvedLanesAsExpired(root, currentTime);693 // Determine the next lanes to work on, and their priority.694 const nextLanes = getNextLanes(695 root,696 root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,697 );698 if (nextLanes === NoLanes) {699 // Special case: There's nothing to work on.700 if (existingCallbackNode !== null) {701 cancelCallback(existingCallbackNode);702 }703 root.callbackNode = null;704 root.callbackPriority = NoLane;705 return;706 }707 // We use the highest priority lane to represent the priority of the callback.708 const newCallbackPriority = getHighestPriorityLane(nextLanes);709 // Check if there's an existing task. We may be able to reuse it.710 const existingCallbackPriority = root.callbackPriority;711 if (712 existingCallbackPriority === newCallbackPriority &&713 // Special case related to `act`. If the currently scheduled task is a714 // Scheduler task, rather than an `act` task, cancel it and re-scheduled715 // on the `act` queue.716 !(717 __DEV__ &&718 ReactCurrentActQueue.current !== null &&719 existingCallbackNode !== fakeActCallbackNode720 )721 ) {722 if (__DEV__) {723 // If we're going to re-use an existing task, it needs to exist.724 // Assume that discrete update microtasks are non-cancellable and null.725 // TODO: Temporary until we confirm this warning is not fired.726 if (727 existingCallbackNode == null &&728 existingCallbackPriority !== SyncLane729 ) {730 console.error(731 'Expected scheduled callback to exist. This error is likely caused by a bug in React. Please file an issue.',732 );733 }734 }735 // The priority hasn't changed. We can reuse the existing task. Exit.736 return;737 }738 if (existingCallbackNode != null) {739 // Cancel the existing callback. We'll schedule a new one below.740 cancelCallback(existingCallbackNode);741 }742 // Schedule a new callback.743 let newCallbackNode;744 if (newCallbackPriority === SyncLane) {745 // Special case: Sync React callbacks are scheduled on a special746 // internal queue747 if (root.tag === LegacyRoot) {748 if (__DEV__ && ReactCurrentActQueue.isBatchingLegacy !== null) {749 ReactCurrentActQueue.didScheduleLegacyUpdate = true;750 }751 scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root));752 } else {753 scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));754 }755 if (supportsMicrotasks) {756 // Flush the queue in a microtask.757 if (__DEV__ && ReactCurrentActQueue.current !== null) {758 // Inside `act`, use our internal `act` queue so that these get flushed759 // at the end of the current scope even when using the sync version760 // of `act`.761 ReactCurrentActQueue.current.push(flushSyncCallbacks);762 } else {763 scheduleMicrotask(() => {764 // In Safari, appending an iframe forces microtasks to run.765 // https://github.com/facebook/react/issues/22459766 // We don't support running callbacks in the middle of render767 // or commit so we need to check against that.768 if (executionContext === NoContext) {769 // It's only safe to do this conditionally because we always770 // check for pending work before we exit the task.771 flushSyncCallbacks();772 }773 });774 }775 } else {776 // Flush the queue in an Immediate task.777 scheduleCallback(ImmediateSchedulerPriority, flushSyncCallbacks);778 }779 newCallbackNode = null;780 } else {781 let schedulerPriorityLevel;782 switch (lanesToEventPriority(nextLanes)) {783 case DiscreteEventPriority:784 schedulerPriorityLevel = ImmediateSchedulerPriority;785 break;786 case ContinuousEventPriority:787 schedulerPriorityLevel = UserBlockingSchedulerPriority;788 break;789 case DefaultEventPriority:790 schedulerPriorityLevel = NormalSchedulerPriority;791 break;792 case IdleEventPriority:793 schedulerPriorityLevel = IdleSchedulerPriority;794 break;795 default:796 schedulerPriorityLevel = NormalSchedulerPriority;797 break;798 }799 newCallbackNode = scheduleCallback(800 schedulerPriorityLevel,801 performConcurrentWorkOnRoot.bind(null, root),802 );803 }804 root.callbackPriority = newCallbackPriority;805 root.callbackNode = newCallbackNode;806}807// This is the entry point for every concurrent task, i.e. anything that808// goes through Scheduler.809function performConcurrentWorkOnRoot(root, didTimeout) {810 if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {811 resetNestedUpdateFlag();812 }813 // Since we know we're in a React event, we can clear the current814 // event time. The next update will compute a new event time.815 currentEventTime = NoTimestamp;816 currentEventTransitionLane = NoLanes;817 if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {818 throw new Error('Should not already be working.');819 }820 // Flush any pending passive effects before deciding which lanes to work on,821 // in case they schedule additional work.822 const originalCallbackNode = root.callbackNode;823 const didFlushPassiveEffects = flushPassiveEffects();824 if (didFlushPassiveEffects) {825 // Something in the passive effect phase may have canceled the current task.826 // Check if the task node for this root was changed.827 if (root.callbackNode !== originalCallbackNode) {828 // The current task was canceled. Exit. We don't need to call829 // `ensureRootIsScheduled` because the check above implies either that830 // there's a new task, or that there's no remaining work on this root.831 return null;832 } else {833 // Current task was not canceled. Continue.834 }835 }836 // Determine the next lanes to work on, using the fields stored837 // on the root.838 let lanes = getNextLanes(839 root,840 root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,841 );842 if (lanes === NoLanes) {843 // Defensive coding. This is never expected to happen.844 return null;845 }846 // We disable time-slicing in some cases: if the work has been CPU-bound847 // for too long ("expired" work, to prevent starvation), or we're in848 // sync-updates-by-default mode.849 // TODO: We only check `didTimeout` defensively, to account for a Scheduler850 // bug we're still investigating. Once the bug in Scheduler is fixed,851 // we can remove this, since we track expiration ourselves.852 const shouldTimeSlice =853 !includesBlockingLane(root, lanes) &&854 !includesExpiredLane(root, lanes) &&855 (disableSchedulerTimeoutInWorkLoop || !didTimeout);856 let exitStatus = shouldTimeSlice857 ? renderRootConcurrent(root, lanes)858 : renderRootSync(root, lanes);859 if (exitStatus !== RootInProgress) {860 if (exitStatus === RootErrored) {861 // If something threw an error, try rendering one more time. We'll862 // render synchronously to block concurrent data mutations, and we'll863 // includes all pending updates are included. If it still fails after864 // the second attempt, we'll give up and commit the resulting tree.865 const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);866 if (errorRetryLanes !== NoLanes) {867 lanes = errorRetryLanes;868 exitStatus = recoverFromConcurrentError(root, errorRetryLanes);869 }870 }871 if (exitStatus === RootFatalErrored) {872 const fatalError = workInProgressRootFatalError;873 prepareFreshStack(root, NoLanes);874 markRootSuspended(root, lanes);875 ensureRootIsScheduled(root, now());876 throw fatalError;877 }878 if (exitStatus === RootDidNotComplete) {879 // The render unwound without completing the tree. This happens in special880 // cases where need to exit the current render without producing a881 // consistent tree or committing.882 //883 // This should only happen during a concurrent render, not a discrete or884 // synchronous update. We should have already checked for this when we885 // unwound the stack.886 markRootSuspended(root, lanes);887 } else {888 // The render completed.889 // Check if this render may have yielded to a concurrent event, and if so,890 // confirm that any newly rendered stores are consistent.891 // TODO: It's possible that even a concurrent render may never have yielded892 // to the main thread, if it was fast enough, or if it expired. We could893 // skip the consistency check in that case, too.894 const renderWasConcurrent = !includesBlockingLane(root, lanes);895 const finishedWork: Fiber = (root.current.alternate: any);896 if (897 renderWasConcurrent &&898 !isRenderConsistentWithExternalStores(finishedWork)899 ) {900 // A store was mutated in an interleaved event. Render again,901 // synchronously, to block further mutations.902 exitStatus = renderRootSync(root, lanes);903 // We need to check again if something threw904 if (exitStatus === RootErrored) {905 const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);906 if (errorRetryLanes !== NoLanes) {907 lanes = errorRetryLanes;908 exitStatus = recoverFromConcurrentError(root, errorRetryLanes);909 // We assume the tree is now consistent because we didn't yield to any910 // concurrent events.911 }912 }913 if (exitStatus === RootFatalErrored) {914 const fatalError = workInProgressRootFatalError;915 prepareFreshStack(root, NoLanes);916 markRootSuspended(root, lanes);917 ensureRootIsScheduled(root, now());918 throw fatalError;919 }920 }921 // We now have a consistent tree. The next step is either to commit it,922 // or, if something suspended, wait to commit it after a timeout.923 root.finishedWork = finishedWork;924 root.finishedLanes = lanes;925 finishConcurrentRender(root, exitStatus, lanes);926 }927 }928 ensureRootIsScheduled(root, now());929 if (root.callbackNode === originalCallbackNode) {930 // The task node scheduled for this root is the same one that's931 // currently executed. Need to return a continuation.932 return performConcurrentWorkOnRoot.bind(null, root);933 }934 return null;935}936function recoverFromConcurrentError(root, errorRetryLanes) {937 // If an error occurred during hydration, discard server response and fall938 // back to client side render.939 if (root.isDehydrated) {940 root.isDehydrated = false;941 if (__DEV__) {942 errorHydratingContainer(root.containerInfo);943 }944 const error = new Error(945 'There was an error while hydrating. Because the error happened outside ' +946 'of a Suspense boundary, the entire root will switch to ' +947 'client rendering.',948 );949 renderDidError(error);950 }951 const errorsFromFirstAttempt = workInProgressRootConcurrentErrors;952 const exitStatus = renderRootSync(root, errorRetryLanes);953 if (exitStatus !== RootErrored) {954 // Successfully finished rendering on retry955 if (errorsFromFirstAttempt !== null) {956 // The errors from the failed first attempt have been recovered. Add957 // them to the collection of recoverable errors. We'll log them in the958 // commit phase.959 queueRecoverableErrors(errorsFromFirstAttempt);960 }961 } else {962 // The UI failed to recover.963 }964 return exitStatus;965}966export function queueRecoverableErrors(errors: Array<mixed>) {967 if (workInProgressRootRecoverableErrors === null) {968 workInProgressRootRecoverableErrors = errors;969 } else {970 workInProgressRootRecoverableErrors.push.apply(971 workInProgressRootRecoverableErrors,972 errors,973 );974 }975}976function finishConcurrentRender(root, exitStatus, lanes) {977 switch (exitStatus) {978 case RootInProgress:979 case RootFatalErrored: {980 throw new Error('Root did not complete. This is a bug in React.');981 }982 // Flow knows about invariant, so it complains if I add a break983 // statement, but eslint doesn't know about invariant, so it complains984 // if I do. eslint-disable-next-line no-fallthrough985 case RootErrored: {986 // We should have already attempted to retry this tree. If we reached987 // this point, it errored again. Commit it.988 commitRoot(root, workInProgressRootRecoverableErrors);989 break;990 }991 case RootSuspended: {992 markRootSuspended(root, lanes);993 // We have an acceptable loading state. We need to figure out if we994 // should immediately commit it or wait a bit.995 if (996 includesOnlyRetries(lanes) &&997 // do not delay if we're inside an act() scope998 !shouldForceFlushFallbacksInDEV()999 ) {1000 // This render only included retries, no updates. Throttle committing1001 // retries so that we don't show too many loading states too quickly.1002 const msUntilTimeout =1003 globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now();1004 // Don't bother with a very short suspense time.1005 if (msUntilTimeout > 10) {1006 const nextLanes = getNextLanes(root, NoLanes);1007 if (nextLanes !== NoLanes) {1008 // There's additional work on this root.1009 break;1010 }1011 const suspendedLanes = root.suspendedLanes;1012 if (!isSubsetOfLanes(suspendedLanes, lanes)) {1013 // We should prefer to render the fallback of at the last1014 // suspended level. Ping the last suspended level to try1015 // rendering it again.1016 // FIXME: What if the suspended lanes are Idle? Should not restart.1017 const eventTime = requestEventTime();1018 markRootPinged(root, suspendedLanes, eventTime);1019 break;1020 }1021 // The render is suspended, it hasn't timed out, and there's no1022 // lower priority work to do. Instead of committing the fallback1023 // immediately, wait for more data to arrive.1024 root.timeoutHandle = scheduleTimeout(1025 commitRoot.bind(null, root, workInProgressRootRecoverableErrors),1026 msUntilTimeout,1027 );1028 break;1029 }1030 }1031 // The work expired. Commit immediately.1032 commitRoot(root, workInProgressRootRecoverableErrors);1033 break;1034 }1035 case RootSuspendedWithDelay: {1036 markRootSuspended(root, lanes);1037 if (includesOnlyTransitions(lanes)) {1038 // This is a transition, so we should exit without committing a1039 // placeholder and without scheduling a timeout. Delay indefinitely1040 // until we receive more data.1041 break;1042 }1043 if (!shouldForceFlushFallbacksInDEV()) {1044 // This is not a transition, but we did trigger an avoided state.1045 // Schedule a placeholder to display after a short delay, using the Just1046 // Noticeable Difference.1047 // TODO: Is the JND optimization worth the added complexity? If this is1048 // the only reason we track the event time, then probably not.1049 // Consider removing.1050 const mostRecentEventTime = getMostRecentEventTime(root, lanes);1051 const eventTimeMs = mostRecentEventTime;1052 const timeElapsedMs = now() - eventTimeMs;1053 const msUntilTimeout = jnd(timeElapsedMs) - timeElapsedMs;1054 // Don't bother with a very short suspense time.1055 if (msUntilTimeout > 10) {1056 // Instead of committing the fallback immediately, wait for more data1057 // to arrive.1058 root.timeoutHandle = scheduleTimeout(1059 commitRoot.bind(null, root, workInProgressRootRecoverableErrors),1060 msUntilTimeout,1061 );1062 break;1063 }1064 }1065 // Commit the placeholder.1066 commitRoot(root, workInProgressRootRecoverableErrors);1067 break;1068 }1069 case RootCompleted: {1070 // The work completed. Ready to commit.1071 commitRoot(root, workInProgressRootRecoverableErrors);1072 break;1073 }1074 default: {1075 throw new Error('Unknown root exit status.');1076 }1077 }1078}1079function isRenderConsistentWithExternalStores(finishedWork: Fiber): boolean {1080 // Search the rendered tree for external store reads, and check whether the1081 // stores were mutated in a concurrent event. Intentionally using an iterative1082 // loop instead of recursion so we can exit early.1083 let node: Fiber = finishedWork;1084 while (true) {1085 if (node.flags & StoreConsistency) {1086 const updateQueue: FunctionComponentUpdateQueue | null = (node.updateQueue: any);1087 if (updateQueue !== null) {1088 const checks = updateQueue.stores;1089 if (checks !== null) {1090 for (let i = 0; i < checks.length; i++) {1091 const check = checks[i];1092 const getSnapshot = check.getSnapshot;1093 const renderedValue = check.value;1094 try {1095 if (!is(getSnapshot(), renderedValue)) {1096 // Found an inconsistent store.1097 return false;1098 }1099 } catch (error) {1100 // If `getSnapshot` throws, return `false`. This will schedule1101 // a re-render, and the error will be rethrown during render.1102 return false;1103 }1104 }1105 }1106 }1107 }1108 const child = node.child;1109 if (node.subtreeFlags & StoreConsistency && child !== null) {1110 child.return = node;1111 node = child;1112 continue;1113 }1114 if (node === finishedWork) {1115 return true;1116 }1117 while (node.sibling === null) {1118 if (node.return === null || node.return === finishedWork) {1119 return true;1120 }1121 node = node.return;1122 }1123 node.sibling.return = node.return;1124 node = node.sibling;1125 }1126 // Flow doesn't know this is unreachable, but eslint does1127 // eslint-disable-next-line no-unreachable1128 return true;1129}1130function markRootSuspended(root, suspendedLanes) {1131 // When suspending, we should always exclude lanes that were pinged or (more1132 // rarely, since we try to avoid it) updated during the render phase.1133 // TODO: Lol maybe there's a better way to factor this besides this1134 // obnoxiously named function :)1135 suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);1136 suspendedLanes = removeLanes(1137 suspendedLanes,1138 workInProgressRootInterleavedUpdatedLanes,1139 );1140 markRootSuspended_dontCallThisOneDirectly(root, suspendedLanes);1141}1142// This is the entry point for synchronous tasks that don't go1143// through Scheduler1144function performSyncWorkOnRoot(root) {1145 if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {1146 syncNestedUpdateFlag();1147 }1148 if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {1149 throw new Error('Should not already be working.');1150 }1151 flushPassiveEffects();1152 let lanes = getNextLanes(root, NoLanes);1153 if (!includesSomeLane(lanes, SyncLane)) {1154 // There's no remaining sync work left.1155 ensureRootIsScheduled(root, now());1156 return null;1157 }1158 let exitStatus = renderRootSync(root, lanes);1159 if (root.tag !== LegacyRoot && exitStatus === RootErrored) {1160 // If something threw an error, try rendering one more time. We'll render1161 // synchronously to block concurrent data mutations, and we'll includes1162 // all pending updates are included. If it still fails after the second1163 // attempt, we'll give up and commit the resulting tree.1164 const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);1165 if (errorRetryLanes !== NoLanes) {1166 lanes = errorRetryLanes;1167 exitStatus = recoverFromConcurrentError(root, errorRetryLanes);1168 }1169 }1170 if (exitStatus === RootFatalErrored) {1171 const fatalError = workInProgressRootFatalError;1172 prepareFreshStack(root, NoLanes);1173 markRootSuspended(root, lanes);1174 ensureRootIsScheduled(root, now());1175 throw fatalError;1176 }1177 if (exitStatus === RootDidNotComplete) {1178 throw new Error('Root did not complete. This is a bug in React.');1179 }1180 // We now have a consistent tree. Because this is a sync render, we1181 // will commit it even if something suspended.1182 const finishedWork: Fiber = (root.current.alternate: any);1183 root.finishedWork = finishedWork;1184 root.finishedLanes = lanes;1185 commitRoot(root, workInProgressRootRecoverableErrors);1186 // Before exiting, make sure there's a callback scheduled for the next1187 // pending level.1188 ensureRootIsScheduled(root, now());1189 return null;1190}1191export function flushRoot(root: FiberRoot, lanes: Lanes) {1192 if (lanes !== NoLanes) {1193 markRootEntangled(root, mergeLanes(lanes, SyncLane));1194 ensureRootIsScheduled(root, now());1195 if ((executionContext & (RenderContext | CommitContext)) === NoContext) {1196 resetRenderTimer();1197 flushSyncCallbacks();1198 }1199 }1200}1201export function getExecutionContext(): ExecutionContext {1202 return executionContext;1203}1204export function deferredUpdates<A>(fn: () => A): A {1205 const previousPriority = getCurrentUpdatePriority();1206 const prevTransition = ReactCurrentBatchConfig.transition;1207 try {1208 ReactCurrentBatchConfig.transition = null;1209 setCurrentUpdatePriority(DefaultEventPriority);1210 return fn();1211 } finally {1212 setCurrentUpdatePriority(previousPriority);1213 ReactCurrentBatchConfig.transition = prevTransition;1214 }1215}1216export function batchedUpdates<A, R>(fn: A => R, a: A): R {1217 const prevExecutionContext = executionContext;1218 executionContext |= BatchedContext;1219 try {1220 return fn(a);1221 } finally {1222 executionContext = prevExecutionContext;1223 // If there were legacy sync updates, flush them at the end of the outer1224 // most batchedUpdates-like method.1225 if (1226 executionContext === NoContext &&1227 // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.1228 !(__DEV__ && ReactCurrentActQueue.isBatchingLegacy)1229 ) {1230 resetRenderTimer();1231 flushSyncCallbacksOnlyInLegacyMode();1232 }1233 }1234}1235export function discreteUpdates<A, B, C, D, R>(1236 fn: (A, B, C, D) => R,1237 a: A,1238 b: B,1239 c: C,1240 d: D,1241): R {1242 const previousPriority = getCurrentUpdatePriority();1243 const prevTransition = ReactCurrentBatchConfig.transition;1244 try {1245 ReactCurrentBatchConfig.transition = null;1246 setCurrentUpdatePriority(DiscreteEventPriority);1247 return fn(a, b, c, d);1248 } finally {1249 setCurrentUpdatePriority(previousPriority);1250 ReactCurrentBatchConfig.transition = prevTransition;1251 if (executionContext === NoContext) {1252 resetRenderTimer();1253 }1254 }1255}1256// Overload the definition to the two valid signatures.1257// Warning, this opts-out of checking the function body.1258declare function flushSync<R>(fn: () => R): R;1259// eslint-disable-next-line no-redeclare1260declare function flushSync(): void;1261// eslint-disable-next-line no-redeclare1262export function flushSync(fn) {1263 // In legacy mode, we flush pending passive effects at the beginning of the1264 // next event, not at the end of the previous one.1265 if (1266 rootWithPendingPassiveEffects !== null &&1267 rootWithPendingPassiveEffects.tag === LegacyRoot &&1268 (executionContext & (RenderContext | CommitContext)) === NoContext1269 ) {1270 flushPassiveEffects();1271 }1272 const prevExecutionContext = executionContext;1273 executionContext |= BatchedContext;1274 const prevTransition = ReactCurrentBatchConfig.transition;1275 const previousPriority = getCurrentUpdatePriority();1276 try {1277 ReactCurrentBatchConfig.transition = null;1278 setCurrentUpdatePriority(DiscreteEventPriority);1279 if (fn) {1280 return fn();1281 } else {1282 return undefined;1283 }1284 } finally {1285 setCurrentUpdatePriority(previousPriority);1286 ReactCurrentBatchConfig.transition = prevTransition;1287 executionContext = prevExecutionContext;1288 // Flush the immediate callbacks that were scheduled during this batch.1289 // Note that this will happen even if batchedUpdates is higher up1290 // the stack.1291 if ((executionContext & (RenderContext | CommitContext)) === NoContext) {1292 flushSyncCallbacks();1293 }1294 }1295}1296export function isAlreadyRendering() {1297 // Used by the renderer to print a warning if certain APIs are called from1298 // the wrong context.1299 return (1300 __DEV__ &&1301 (executionContext & (RenderContext | CommitContext)) !== NoContext1302 );1303}1304export function flushControlled(fn: () => mixed): void {1305 const prevExecutionContext = executionContext;1306 executionContext |= BatchedContext;1307 const prevTransition = ReactCurrentBatchConfig.transition;1308 const previousPriority = getCurrentUpdatePriority();1309 try {1310 ReactCurrentBatchConfig.transition = null;1311 setCurrentUpdatePriority(DiscreteEventPriority);1312 fn();1313 } finally {1314 setCurrentUpdatePriority(previousPriority);1315 ReactCurrentBatchConfig.transition = prevTransition;1316 executionContext = prevExecutionContext;1317 if (executionContext === NoContext) {1318 // Flush the immediate callbacks that were scheduled during this batch1319 resetRenderTimer();1320 flushSyncCallbacks();1321 }1322 }1323}1324export function pushRenderLanes(fiber: Fiber, lanes: Lanes) {1325 pushToStack(subtreeRenderLanesCursor, subtreeRenderLanes, fiber);1326 subtreeRenderLanes = mergeLanes(subtreeRenderLanes, lanes);1327 workInProgressRootIncludedLanes = mergeLanes(1328 workInProgressRootIncludedLanes,1329 lanes,1330 );1331}1332export function popRenderLanes(fiber: Fiber) {1333 subtreeRenderLanes = subtreeRenderLanesCursor.current;1334 popFromStack(subtreeRenderLanesCursor, fiber);1335}1336function prepareFreshStack(root: FiberRoot, lanes: Lanes) {1337 root.finishedWork = null;1338 root.finishedLanes = NoLanes;1339 const timeoutHandle = root.timeoutHandle;1340 if (timeoutHandle !== noTimeout) {1341 // The root previous suspended and scheduled a timeout to commit a fallback1342 // state. Now that we have additional work, cancel the timeout.1343 root.timeoutHandle = noTimeout;1344 // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above1345 cancelTimeout(timeoutHandle);1346 }1347 if (workInProgress !== null) {1348 let interruptedWork = workInProgress.return;1349 while (interruptedWork !== null) {1350 const current = interruptedWork.alternate;1351 unwindInterruptedWork(1352 current,1353 interruptedWork,1354 workInProgressRootRenderLanes,1355 );1356 interruptedWork = interruptedWork.return;1357 }1358 }1359 workInProgressRoot = root;1360 workInProgress = createWorkInProgress(root.current, null);1361 workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;1362 workInProgressRootExitStatus = RootInProgress;1363 workInProgressRootFatalError = null;1364 workInProgressRootSkippedLanes = NoLanes;1365 workInProgressRootInterleavedUpdatedLanes = NoLanes;1366 workInProgressRootRenderPhaseUpdatedLanes = NoLanes;1367 workInProgressRootPingedLanes = NoLanes;1368 workInProgressRootConcurrentErrors = null;1369 workInProgressRootRecoverableErrors = null;1370 enqueueInterleavedUpdates();1371 if (__DEV__) {1372 ReactStrictModeWarnings.discardPendingWarnings();1373 }1374}1375function handleError(root, thrownValue): void {1376 do {1377 let erroredWork = workInProgress;1378 try {1379 // Reset module-level state that was set during the render phase.1380 resetContextDependencies();1381 resetHooksAfterThrow();1382 resetCurrentDebugFiberInDEV();1383 // TODO: I found and added this missing line while investigating a1384 // separate issue. Write a regression test using string refs.1385 ReactCurrentOwner.current = null;1386 if (erroredWork === null || erroredWork.return === null) {1387 // Expected to be working on a non-root fiber. This is a fatal error1388 // because there's no ancestor that can handle it; the root is1389 // supposed to capture all errors that weren't caught by an error1390 // boundary.1391 workInProgressRootExitStatus = RootFatalErrored;1392 workInProgressRootFatalError = thrownValue;1393 // Set `workInProgress` to null. This represents advancing to the next1394 // sibling, or the parent if there are no siblings. But since the root1395 // has no siblings nor a parent, we set it to null. Usually this is1396 // handled by `completeUnitOfWork` or `unwindWork`, but since we're1397 // intentionally not calling those, we need set it here.1398 // TODO: Consider calling `unwindWork` to pop the contexts.1399 workInProgress = null;1400 return;1401 }1402 if (enableProfilerTimer && erroredWork.mode & ProfileMode) {1403 // Record the time spent rendering before an error was thrown. This1404 // avoids inaccurate Profiler durations in the case of a1405 // suspended render.1406 stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true);1407 }1408 if (enableSchedulingProfiler) {1409 markComponentRenderStopped();1410 if (1411 thrownValue !== null &&1412 typeof thrownValue === 'object' &&1413 typeof thrownValue.then === 'function'1414 ) {1415 const wakeable: Wakeable = (thrownValue: any);1416 markComponentSuspended(1417 erroredWork,1418 wakeable,1419 workInProgressRootRenderLanes,1420 );1421 } else {1422 markComponentErrored(1423 erroredWork,1424 thrownValue,1425 workInProgressRootRenderLanes,1426 );1427 }1428 }1429 throwException(1430 root,1431 erroredWork.return,1432 erroredWork,1433 thrownValue,1434 workInProgressRootRenderLanes,1435 );1436 completeUnitOfWork(erroredWork);1437 } catch (yetAnotherThrownValue) {1438 // Something in the return path also threw.1439 thrownValue = yetAnotherThrownValue;1440 if (workInProgress === erroredWork && erroredWork !== null) {1441 // If this boundary has already errored, then we had trouble processing1442 // the error. Bubble it to the next boundary.1443 erroredWork = erroredWork.return;1444 workInProgress = erroredWork;1445 } else {1446 erroredWork = workInProgress;1447 }1448 continue;1449 }1450 // Return to the normal work loop.1451 return;1452 } while (true);1453}1454function pushDispatcher() {1455 const prevDispatcher = ReactCurrentDispatcher.current;1456 ReactCurrentDispatcher.current = ContextOnlyDispatcher;1457 if (prevDispatcher === null) {1458 // The React isomorphic package does not include a default dispatcher.1459 // Instead the first renderer will lazily attach one, in order to give1460 // nicer error messages.1461 return ContextOnlyDispatcher;1462 } else {1463 return prevDispatcher;1464 }1465}1466function popDispatcher(prevDispatcher) {1467 ReactCurrentDispatcher.current = prevDispatcher;1468}1469export function markCommitTimeOfFallback() {1470 globalMostRecentFallbackTime = now();1471}1472export function markSkippedUpdateLanes(lane: Lane | Lanes): void {1473 workInProgressRootSkippedLanes = mergeLanes(1474 lane,1475 workInProgressRootSkippedLanes,1476 );1477}1478export function renderDidSuspend(): void {1479 if (workInProgressRootExitStatus === RootInProgress) {1480 workInProgressRootExitStatus = RootSuspended;1481 }1482}1483export function renderDidSuspendDelayIfPossible(): void {1484 if (1485 workInProgressRootExitStatus === RootInProgress ||1486 workInProgressRootExitStatus === RootSuspended ||1487 workInProgressRootExitStatus === RootErrored1488 ) {1489 workInProgressRootExitStatus = RootSuspendedWithDelay;1490 }1491 // Check if there are updates that we skipped tree that might have unblocked1492 // this render.1493 if (1494 workInProgressRoot !== null &&1495 (includesNonIdleWork(workInProgressRootSkippedLanes) ||1496 includesNonIdleWork(workInProgressRootInterleavedUpdatedLanes))1497 ) {1498 // Mark the current render as suspended so that we switch to working on1499 // the updates that were skipped. Usually we only suspend at the end of1500 // the render phase.1501 // TODO: We should probably always mark the root as suspended immediately1502 // (inside this function), since by suspending at the end of the render1503 // phase introduces a potential mistake where we suspend lanes that were1504 // pinged or updated while we were rendering.1505 markRootSuspended(workInProgressRoot, workInProgressRootRenderLanes);1506 }1507}1508export function renderDidError(error: mixed) {1509 if (workInProgressRootExitStatus !== RootSuspendedWithDelay) {1510 workInProgressRootExitStatus = RootErrored;1511 }1512 if (workInProgressRootConcurrentErrors === null) {1513 workInProgressRootConcurrentErrors = [error];1514 } else {1515 workInProgressRootConcurrentErrors.push(error);1516 }1517}1518// Called during render to determine if anything has suspended.1519// Returns false if we're not sure.1520export function renderHasNotSuspendedYet(): boolean {1521 // If something errored or completed, we can't really be sure,1522 // so those are false.1523 return workInProgressRootExitStatus === RootInProgress;1524}1525function renderRootSync(root: FiberRoot, lanes: Lanes) {1526 const prevExecutionContext = executionContext;1527 executionContext |= RenderContext;1528 const prevDispatcher = pushDispatcher();1529 // If the root or lanes have changed, throw out the existing stack1530 // and prepare a fresh one. Otherwise we'll continue where we left off.1531 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1532 if (enableUpdaterTracking) {1533 if (isDevToolsPresent) {1534 const memoizedUpdaters = root.memoizedUpdaters;1535 if (memoizedUpdaters.size > 0) {1536 restorePendingUpdaters(root, workInProgressRootRenderLanes);1537 memoizedUpdaters.clear();1538 }1539 // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.1540 // If we bailout on this work, we'll move them back (like above).1541 // It's important to move them now in case the work spawns more work at the same priority with different updaters.1542 // That way we can keep the current update and future updates separate.1543 movePendingFibersToMemoized(root, lanes);1544 }1545 }1546 workInProgressTransitions = getTransitionsForLanes(root, lanes);1547 prepareFreshStack(root, lanes);1548 }1549 if (__DEV__) {1550 if (enableDebugTracing) {1551 logRenderStarted(lanes);1552 }1553 }1554 if (enableSchedulingProfiler) {1555 markRenderStarted(lanes);1556 }1557 do {1558 try {1559 workLoopSync();1560 break;1561 } catch (thrownValue) {1562 handleError(root, thrownValue);1563 }1564 } while (true);1565 resetContextDependencies();1566 executionContext = prevExecutionContext;1567 popDispatcher(prevDispatcher);1568 if (workInProgress !== null) {1569 // This is a sync render, so we should have finished the whole tree.1570 throw new Error(1571 'Cannot commit an incomplete root. This error is likely caused by a ' +1572 'bug in React. Please file an issue.',1573 );1574 }1575 if (__DEV__) {1576 if (enableDebugTracing) {1577 logRenderStopped();1578 }1579 }1580 if (enableSchedulingProfiler) {1581 markRenderStopped();1582 }1583 // Set this to null to indicate there's no in-progress render.1584 workInProgressRoot = null;1585 workInProgressRootRenderLanes = NoLanes;1586 return workInProgressRootExitStatus;1587}1588// The work loop is an extremely hot path. Tell Closure not to inline it.1589/** @noinline */1590function workLoopSync() {1591 // Already timed out, so perform work without checking if we need to yield.1592 while (workInProgress !== null) {1593 performUnitOfWork(workInProgress);1594 }1595}1596function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {1597 const prevExecutionContext = executionContext;1598 executionContext |= RenderContext;1599 const prevDispatcher = pushDispatcher();1600 // If the root or lanes have changed, throw out the existing stack1601 // and prepare a fresh one. Otherwise we'll continue where we left off.1602 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1603 if (enableUpdaterTracking) {1604 if (isDevToolsPresent) {1605 const memoizedUpdaters = root.memoizedUpdaters;1606 if (memoizedUpdaters.size > 0) {1607 restorePendingUpdaters(root, workInProgressRootRenderLanes);1608 memoizedUpdaters.clear();1609 }1610 // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.1611 // If we bailout on this work, we'll move them back (like above).1612 // It's important to move them now in case the work spawns more work at the same priority with different updaters.1613 // That way we can keep the current update and future updates separate.1614 movePendingFibersToMemoized(root, lanes);1615 }1616 }1617 workInProgressTransitions = getTransitionsForLanes(root, lanes);1618 resetRenderTimer();1619 prepareFreshStack(root, lanes);1620 }1621 if (__DEV__) {1622 if (enableDebugTracing) {1623 logRenderStarted(lanes);1624 }1625 }1626 if (enableSchedulingProfiler) {1627 markRenderStarted(lanes);1628 }1629 do {1630 try {1631 workLoopConcurrent();1632 break;1633 } catch (thrownValue) {1634 handleError(root, thrownValue);1635 }1636 } while (true);1637 resetContextDependencies();1638 popDispatcher(prevDispatcher);1639 executionContext = prevExecutionContext;1640 if (__DEV__) {1641 if (enableDebugTracing) {1642 logRenderStopped();1643 }1644 }1645 // Check if the tree has completed.1646 if (workInProgress !== null) {1647 // Still work remaining.1648 if (enableSchedulingProfiler) {1649 markRenderYielded();1650 }1651 return RootInProgress;1652 } else {1653 // Completed the tree.1654 if (enableSchedulingProfiler) {1655 markRenderStopped();1656 }1657 // Set this to null to indicate there's no in-progress render.1658 workInProgressRoot = null;1659 workInProgressRootRenderLanes = NoLanes;1660 // Return the final exit status.1661 return workInProgressRootExitStatus;1662 }1663}1664/** @noinline */1665function workLoopConcurrent() {1666 // Perform work until Scheduler asks us to yield1667 while (workInProgress !== null && !shouldYield()) {1668 performUnitOfWork(workInProgress);1669 }1670}1671function performUnitOfWork(unitOfWork: Fiber): void {1672 // The current, flushed, state of this fiber is the alternate. Ideally1673 // nothing should rely on this, but relying on it here means that we don't1674 // need an additional field on the work in progress.1675 const current = unitOfWork.alternate;1676 setCurrentDebugFiberInDEV(unitOfWork);1677 let next;1678 if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {1679 startProfilerTimer(unitOfWork);1680 next = beginWork(current, unitOfWork, subtreeRenderLanes);1681 stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);1682 } else {1683 next = beginWork(current, unitOfWork, subtreeRenderLanes);1684 }1685 resetCurrentDebugFiberInDEV();1686 unitOfWork.memoizedProps = unitOfWork.pendingProps;1687 if (next === null) {1688 // If this doesn't spawn new work, complete the current work.1689 completeUnitOfWork(unitOfWork);1690 } else {1691 workInProgress = next;1692 }1693 ReactCurrentOwner.current = null;1694}1695function completeUnitOfWork(unitOfWork: Fiber): void {1696 // Attempt to complete the current unit of work, then move to the next1697 // sibling. If there are no more siblings, return to the parent fiber.1698 let completedWork = unitOfWork;1699 do {1700 // The current, flushed, state of this fiber is the alternate. Ideally1701 // nothing should rely on this, but relying on it here means that we don't1702 // need an additional field on the work in progress.1703 const current = completedWork.alternate;1704 const returnFiber = completedWork.return;1705 // Check if the work completed or if something threw.1706 if ((completedWork.flags & Incomplete) === NoFlags) {1707 setCurrentDebugFiberInDEV(completedWork);1708 let next;1709 if (1710 !enableProfilerTimer ||1711 (completedWork.mode & ProfileMode) === NoMode1712 ) {1713 next = completeWork(current, completedWork, subtreeRenderLanes);1714 } else {1715 startProfilerTimer(completedWork);1716 next = completeWork(current, completedWork, subtreeRenderLanes);1717 // Update render duration assuming we didn't error.1718 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1719 }1720 resetCurrentDebugFiberInDEV();1721 if (next !== null) {1722 // Completing this fiber spawned new work. Work on that next.1723 workInProgress = next;1724 return;1725 }1726 } else {1727 // This fiber did not complete because something threw. Pop values off1728 // the stack without entering the complete phase. If this is a boundary,1729 // capture values if possible.1730 const next = unwindWork(current, completedWork, subtreeRenderLanes);1731 // Because this fiber did not complete, don't reset its lanes.1732 if (next !== null) {1733 // If completing this work spawned new work, do that next. We'll come1734 // back here again.1735 // Since we're restarting, remove anything that is not a host effect1736 // from the effect tag.1737 next.flags &= HostEffectMask;1738 workInProgress = next;1739 return;1740 }1741 if (1742 enableProfilerTimer &&1743 (completedWork.mode & ProfileMode) !== NoMode1744 ) {1745 // Record the render duration for the fiber that errored.1746 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1747 // Include the time spent working on failed children before continuing.1748 let actualDuration = completedWork.actualDuration;1749 let child = completedWork.child;1750 while (child !== null) {1751 actualDuration += child.actualDuration;1752 child = child.sibling;1753 }1754 completedWork.actualDuration = actualDuration;1755 }1756 if (returnFiber !== null) {1757 // Mark the parent fiber as incomplete and clear its subtree flags.1758 returnFiber.flags |= Incomplete;1759 returnFiber.subtreeFlags = NoFlags;1760 returnFiber.deletions = null;1761 } else {1762 // We've unwound all the way to the root.1763 workInProgressRootExitStatus = RootDidNotComplete;1764 workInProgress = null;1765 return;1766 }1767 }1768 const siblingFiber = completedWork.sibling;1769 if (siblingFiber !== null) {1770 // If there is more work to do in this returnFiber, do that next.1771 workInProgress = siblingFiber;1772 return;1773 }1774 // Otherwise, return to the parent1775 completedWork = returnFiber;1776 // Update the next thing we're working on in case something throws.1777 workInProgress = completedWork;1778 } while (completedWork !== null);1779 // We've reached the root.1780 if (workInProgressRootExitStatus === RootInProgress) {1781 workInProgressRootExitStatus = RootCompleted;1782 }1783}1784function commitRoot(root: FiberRoot, recoverableErrors: null | Array<mixed>) {1785 // TODO: This no longer makes any sense. We already wrap the mutation and1786 // layout phases. Should be able to remove.1787 const previousUpdateLanePriority = getCurrentUpdatePriority();1788 const prevTransition = ReactCurrentBatchConfig.transition;1789 try {1790 ReactCurrentBatchConfig.transition = null;1791 setCurrentUpdatePriority(DiscreteEventPriority);1792 commitRootImpl(root, recoverableErrors, previousUpdateLanePriority);1793 } finally {1794 ReactCurrentBatchConfig.transition = prevTransition;1795 setCurrentUpdatePriority(previousUpdateLanePriority);1796 }1797 return null;1798}1799function commitRootImpl(1800 root: FiberRoot,1801 recoverableErrors: null | Array<mixed>,1802 renderPriorityLevel: EventPriority,1803) {1804 do {1805 // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which1806 // means `flushPassiveEffects` will sometimes result in additional1807 // passive effects. So we need to keep flushing in a loop until there are1808 // no more pending effects.1809 // TODO: Might be better if `flushPassiveEffects` did not automatically1810 // flush synchronous work at the end, to avoid factoring hazards like this.1811 flushPassiveEffects();1812 } while (rootWithPendingPassiveEffects !== null);1813 flushRenderPhaseStrictModeWarningsInDEV();1814 if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {1815 throw new Error('Should not already be working.');1816 }1817 const finishedWork = root.finishedWork;1818 const lanes = root.finishedLanes;1819 if (__DEV__) {1820 if (enableDebugTracing) {1821 logCommitStarted(lanes);1822 }1823 }1824 if (enableSchedulingProfiler) {1825 markCommitStarted(lanes);1826 }1827 if (finishedWork === null) {1828 if (__DEV__) {1829 if (enableDebugTracing) {1830 logCommitStopped();1831 }1832 }1833 if (enableSchedulingProfiler) {1834 markCommitStopped();1835 }1836 return null;1837 } else {1838 if (__DEV__) {1839 if (lanes === NoLanes) {1840 console.error(1841 'root.finishedLanes should not be empty during a commit. This is a ' +1842 'bug in React.',1843 );1844 }1845 }1846 }1847 root.finishedWork = null;1848 root.finishedLanes = NoLanes;1849 if (finishedWork === root.current) {1850 throw new Error(1851 'Cannot commit the same tree as before. This error is likely caused by ' +1852 'a bug in React. Please file an issue.',1853 );1854 }1855 // commitRoot never returns a continuation; it always finishes synchronously.1856 // So we can clear these now to allow a new callback to be scheduled.1857 root.callbackNode = null;1858 root.callbackPriority = NoLane;1859 // Update the first and last pending times on this root. The new first1860 // pending time is whatever is left on the root fiber.1861 let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);1862 markRootFinished(root, remainingLanes);1863 if (root === workInProgressRoot) {1864 // We can reset these now that they are finished.1865 workInProgressRoot = null;1866 workInProgress = null;1867 workInProgressRootRenderLanes = NoLanes;1868 } else {1869 // This indicates that the last root we worked on is not the same one that1870 // we're committing now. This most commonly happens when a suspended root1871 // times out.1872 }1873 // If there are pending passive effects, schedule a callback to process them.1874 // Do this as early as possible, so it is queued before anything else that1875 // might get scheduled in the commit phase. (See #16714.)1876 // TODO: Delete all other places that schedule the passive effect callback1877 // They're redundant.1878 if (1879 (finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||1880 (finishedWork.flags & PassiveMask) !== NoFlags1881 ) {1882 if (!rootDoesHavePassiveEffects) {1883 rootDoesHavePassiveEffects = true;1884 pendingPassiveEffectsRemainingLanes = remainingLanes;1885 scheduleCallback(NormalSchedulerPriority, () => {1886 flushPassiveEffects();1887 // This render triggered passive effects: release the root cache pool1888 // *after* passive effects fire to avoid freeing a cache pool that may1889 // be referenced by a node in the tree (HostRoot, Cache boundary etc)1890 return null;1891 });1892 }1893 }1894 // Check if there are any effects in the whole tree.1895 // TODO: This is left over from the effect list implementation, where we had1896 // to check for the existence of `firstEffect` to satisfy Flow. I think the1897 // only other reason this optimization exists is because it affects profiling.1898 // Reconsider whether this is necessary.1899 const subtreeHasEffects =1900 (finishedWork.subtreeFlags &1901 (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1902 NoFlags;1903 const rootHasEffect =1904 (finishedWork.flags &1905 (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1906 NoFlags;1907 if (subtreeHasEffects || rootHasEffect) {1908 const prevTransition = ReactCurrentBatchConfig.transition;1909 ReactCurrentBatchConfig.transition = null;1910 const previousPriority = getCurrentUpdatePriority();1911 setCurrentUpdatePriority(DiscreteEventPriority);1912 const prevExecutionContext = executionContext;1913 executionContext |= CommitContext;1914 // Reset this to null before calling lifecycles1915 ReactCurrentOwner.current = null;1916 // The commit phase is broken into several sub-phases. We do a separate pass1917 // of the effect list for each phase: all mutation effects come before all1918 // layout effects, and so on.1919 // The first phase a "before mutation" phase. We use this phase to read the1920 // state of the host tree right before we mutate it. This is where1921 // getSnapshotBeforeUpdate is called.1922 const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(1923 root,1924 finishedWork,1925 );1926 if (enableProfilerTimer) {1927 // Mark the current commit time to be shared by all Profilers in this1928 // batch. This enables them to be grouped later.1929 recordCommitTime();1930 }1931 if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {1932 // Track the root here, rather than in commitLayoutEffects(), because of ref setters.1933 // Updates scheduled during ref detachment should also be flagged.1934 rootCommittingMutationOrLayoutEffects = root;1935 }1936 // The next phase is the mutation phase, where we mutate the host tree.1937 commitMutationEffects(root, finishedWork, lanes);1938 if (enableCreateEventHandleAPI) {1939 if (shouldFireAfterActiveInstanceBlur) {1940 afterActiveInstanceBlur();1941 }1942 }1943 resetAfterCommit(root.containerInfo);1944 // The work-in-progress tree is now the current tree. This must come after1945 // the mutation phase, so that the previous tree is still current during1946 // componentWillUnmount, but before the layout phase, so that the finished1947 // work is current during componentDidMount/Update.1948 root.current = finishedWork;1949 // The next phase is the layout phase, where we call effects that read1950 // the host tree after it's been mutated. The idiomatic use case for this is1951 // layout, but class component lifecycles also fire here for legacy reasons.1952 if (__DEV__) {1953 if (enableDebugTracing) {1954 logLayoutEffectsStarted(lanes);1955 }1956 }1957 if (enableSchedulingProfiler) {1958 markLayoutEffectsStarted(lanes);1959 }1960 commitLayoutEffects(finishedWork, root, lanes);1961 if (__DEV__) {1962 if (enableDebugTracing) {1963 logLayoutEffectsStopped();1964 }1965 }1966 if (enableSchedulingProfiler) {1967 markLayoutEffectsStopped();1968 }1969 if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {1970 rootCommittingMutationOrLayoutEffects = null;1971 }1972 // Tell Scheduler to yield at the end of the frame, so the browser has an1973 // opportunity to paint.1974 requestPaint();1975 executionContext = prevExecutionContext;1976 // Reset the priority to the previous non-sync value.1977 setCurrentUpdatePriority(previousPriority);1978 ReactCurrentBatchConfig.transition = prevTransition;1979 } else {1980 // No effects.1981 root.current = finishedWork;1982 // Measure these anyway so the flamegraph explicitly shows that there were1983 // no effects.1984 // TODO: Maybe there's a better way to report this.1985 if (enableProfilerTimer) {1986 recordCommitTime();1987 }1988 }1989 const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;1990 if (rootDoesHavePassiveEffects) {1991 // This commit has passive effects. Stash a reference to them. But don't1992 // schedule a callback until after flushing layout work.1993 rootDoesHavePassiveEffects = false;1994 rootWithPendingPassiveEffects = root;1995 pendingPassiveEffectsLanes = lanes;1996 } else {1997 // There were no passive effects, so we can immediately release the cache1998 // pool for this render.1999 releaseRootPooledCache(root, remainingLanes);2000 }2001 // Read this again, since an effect might have updated it2002 remainingLanes = root.pendingLanes;2003 // Check if there's remaining work on this root2004 // TODO: This is part of the `componentDidCatch` implementation. Its purpose2005 // is to detect whether something might have called setState inside2006 // `componentDidCatch`. The mechanism is known to be flawed because `setState`2007 // inside `componentDidCatch` is itself flawed â that's why we recommend2008 // `getDerivedStateFromError` instead. However, it could be improved by2009 // checking if remainingLanes includes Sync work, instead of whether there's2010 // any work remaining at all (which would also include stuff like Suspense2011 // retries or transitions). It's been like this for a while, though, so fixing2012 // it probably isn't that urgent.2013 if (remainingLanes === NoLanes) {2014 // If there's no remaining work, we can clear the set of already failed2015 // error boundaries.2016 legacyErrorBoundariesThatAlreadyFailed = null;2017 }2018 if (__DEV__ && enableStrictEffects) {2019 if (!rootDidHavePassiveEffects) {2020 commitDoubleInvokeEffectsInDEV(root.current, false);2021 }2022 }2023 onCommitRootDevTools(finishedWork.stateNode, renderPriorityLevel);2024 if (enableUpdaterTracking) {2025 if (isDevToolsPresent) {2026 root.memoizedUpdaters.clear();2027 }2028 }2029 if (__DEV__) {2030 onCommitRootTestSelector();2031 }2032 // Always call this before exiting `commitRoot`, to ensure that any2033 // additional work on this root is scheduled.2034 ensureRootIsScheduled(root, now());2035 if (recoverableErrors !== null) {2036 // There were errors during this render, but recovered from them without2037 // needing to surface it to the UI. We log them here.2038 const onRecoverableError = root.onRecoverableError;2039 for (let i = 0; i < recoverableErrors.length; i++) {2040 const recoverableError = recoverableErrors[i];2041 onRecoverableError(recoverableError);2042 }2043 }2044 if (hasUncaughtError) {2045 hasUncaughtError = false;2046 const error = firstUncaughtError;2047 firstUncaughtError = null;2048 throw error;2049 }2050 // If the passive effects are the result of a discrete render, flush them2051 // synchronously at the end of the current task so that the result is2052 // immediately observable. Otherwise, we assume that they are not2053 // order-dependent and do not need to be observed by external systems, so we2054 // can wait until after paint.2055 // TODO: We can optimize this by not scheduling the callback earlier. Since we2056 // currently schedule the callback in multiple places, will wait until those2057 // are consolidated.2058 if (2059 includesSomeLane(pendingPassiveEffectsLanes, SyncLane) &&2060 root.tag !== LegacyRoot2061 ) {2062 flushPassiveEffects();2063 }2064 // Read this again, since a passive effect might have updated it2065 remainingLanes = root.pendingLanes;2066 if (includesSomeLane(remainingLanes, (SyncLane: Lane))) {2067 if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {2068 markNestedUpdateScheduled();2069 }2070 // Count the number of times the root synchronously re-renders without2071 // finishing. If there are too many, it indicates an infinite update loop.2072 if (root === rootWithNestedUpdates) {2073 nestedUpdateCount++;2074 } else {2075 nestedUpdateCount = 0;2076 rootWithNestedUpdates = root;2077 }2078 } else {2079 nestedUpdateCount = 0;2080 }2081 // If layout work was scheduled, flush it now.2082 flushSyncCallbacks();2083 if (enableTransitionTracing) {2084 const prevPendingTransitionCallbacks = currentPendingTransitionCallbacks;2085 const prevRootTransitionCallbacks = root.transitionCallbacks;2086 if (2087 prevPendingTransitionCallbacks !== null &&2088 prevRootTransitionCallbacks !== null2089 ) {2090 // TODO(luna) Refactor this code into the Host Config2091 const endTime = now();2092 currentPendingTransitionCallbacks = null;2093 scheduleCallback(IdleSchedulerPriority, () =>2094 processTransitionCallbacks(2095 prevPendingTransitionCallbacks,2096 endTime,2097 prevRootTransitionCallbacks,2098 ),2099 );2100 }2101 }2102 if (__DEV__) {2103 if (enableDebugTracing) {2104 logCommitStopped();2105 }2106 }2107 if (enableSchedulingProfiler) {2108 markCommitStopped();2109 }2110 return null;2111}2112function releaseRootPooledCache(root: FiberRoot, remainingLanes: Lanes) {2113 if (enableCache) {2114 const pooledCacheLanes = (root.pooledCacheLanes &= remainingLanes);2115 if (pooledCacheLanes === NoLanes) {2116 // None of the remaining work relies on the cache pool. Clear it so2117 // subsequent requests get a new cache2118 const pooledCache = root.pooledCache;2119 if (pooledCache != null) {2120 root.pooledCache = null;2121 releaseCache(pooledCache);2122 }2123 }2124 }2125}2126export function flushPassiveEffects(): boolean {2127 // Returns whether passive effects were flushed.2128 // TODO: Combine this check with the one in flushPassiveEFfectsImpl. We should2129 // probably just combine the two functions. I believe they were only separate2130 // in the first place because we used to wrap it with2131 // `Scheduler.runWithPriority`, which accepts a function. But now we track the2132 // priority within React itself, so we can mutate the variable directly.2133 if (rootWithPendingPassiveEffects !== null) {2134 // Cache the root since rootWithPendingPassiveEffects is cleared in2135 // flushPassiveEffectsImpl2136 const root = rootWithPendingPassiveEffects;2137 // Cache and clear the remaining lanes flag; it must be reset since this2138 // method can be called from various places, not always from commitRoot2139 // where the remaining lanes are known2140 const remainingLanes = pendingPassiveEffectsRemainingLanes;2141 pendingPassiveEffectsRemainingLanes = NoLanes;2142 const renderPriority = lanesToEventPriority(pendingPassiveEffectsLanes);2143 const priority = lowerEventPriority(DefaultEventPriority, renderPriority);2144 const prevTransition = ReactCurrentBatchConfig.transition;2145 const previousPriority = getCurrentUpdatePriority();2146 try {2147 ReactCurrentBatchConfig.transition = null;2148 setCurrentUpdatePriority(priority);2149 return flushPassiveEffectsImpl();2150 } finally {2151 setCurrentUpdatePriority(previousPriority);2152 ReactCurrentBatchConfig.transition = prevTransition;2153 // Once passive effects have run for the tree - giving components a2154 // chance to retain cache instances they use - release the pooled2155 // cache at the root (if there is one)2156 releaseRootPooledCache(root, remainingLanes);2157 }2158 }2159 return false;2160}2161export function enqueuePendingPassiveProfilerEffect(fiber: Fiber): void {2162 if (enableProfilerTimer && enableProfilerCommitHooks) {2163 pendingPassiveProfilerEffects.push(fiber);2164 if (!rootDoesHavePassiveEffects) {2165 rootDoesHavePassiveEffects = true;2166 scheduleCallback(NormalSchedulerPriority, () => {2167 flushPassiveEffects();2168 return null;2169 });2170 }2171 }2172}2173function flushPassiveEffectsImpl() {2174 if (rootWithPendingPassiveEffects === null) {2175 return false;2176 }2177 const root = rootWithPendingPassiveEffects;2178 const lanes = pendingPassiveEffectsLanes;2179 rootWithPendingPassiveEffects = null;2180 // TODO: This is sometimes out of sync with rootWithPendingPassiveEffects.2181 // Figure out why and fix it. It's not causing any known issues (probably2182 // because it's only used for profiling), but it's a refactor hazard.2183 pendingPassiveEffectsLanes = NoLanes;2184 if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {2185 throw new Error('Cannot flush passive effects while already rendering.');2186 }2187 if (__DEV__) {2188 if (enableDebugTracing) {2189 logPassiveEffectsStarted(lanes);2190 }2191 }2192 if (enableSchedulingProfiler) {2193 markPassiveEffectsStarted(lanes);2194 }2195 const prevExecutionContext = executionContext;2196 executionContext |= CommitContext;2197 commitPassiveUnmountEffects(root.current);2198 commitPassiveMountEffects(root, root.current);2199 // TODO: Move to commitPassiveMountEffects2200 if (enableProfilerTimer && enableProfilerCommitHooks) {2201 const profilerEffects = pendingPassiveProfilerEffects;2202 pendingPassiveProfilerEffects = [];2203 for (let i = 0; i < profilerEffects.length; i++) {2204 const fiber = ((profilerEffects[i]: any): Fiber);2205 commitPassiveEffectDurations(root, fiber);2206 }2207 }2208 if (__DEV__) {2209 if (enableDebugTracing) {2210 logPassiveEffectsStopped();2211 }2212 }2213 if (enableSchedulingProfiler) {2214 markPassiveEffectsStopped();2215 }2216 if (__DEV__ && enableStrictEffects) {2217 commitDoubleInvokeEffectsInDEV(root.current, true);2218 }2219 executionContext = prevExecutionContext;2220 flushSyncCallbacks();2221 // If additional passive effects were scheduled, increment a counter. If this2222 // exceeds the limit, we'll fire a warning.2223 nestedPassiveUpdateCount =2224 rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;2225 // TODO: Move to commitPassiveMountEffects2226 onPostCommitRootDevTools(root);2227 if (enableProfilerTimer && enableProfilerCommitHooks) {2228 const stateNode = root.current.stateNode;2229 stateNode.effectDuration = 0;2230 stateNode.passiveEffectDuration = 0;2231 }2232 return true;2233}2234export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {2235 return (2236 legacyErrorBoundariesThatAlreadyFailed !== null &&2237 legacyErrorBoundariesThatAlreadyFailed.has(instance)2238 );2239}2240export function markLegacyErrorBoundaryAsFailed(instance: mixed) {2241 if (legacyErrorBoundariesThatAlreadyFailed === null) {2242 legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);2243 } else {2244 legacyErrorBoundariesThatAlreadyFailed.add(instance);2245 }2246}2247function prepareToThrowUncaughtError(error: mixed) {2248 if (!hasUncaughtError) {2249 hasUncaughtError = true;2250 firstUncaughtError = error;2251 }2252}2253export const onUncaughtError = prepareToThrowUncaughtError;2254function captureCommitPhaseErrorOnRoot(2255 rootFiber: Fiber,2256 sourceFiber: Fiber,2257 error: mixed,2258) {2259 const errorInfo = createCapturedValue(error, sourceFiber);2260 const update = createRootErrorUpdate(rootFiber, errorInfo, (SyncLane: Lane));2261 enqueueUpdate(rootFiber, update, (SyncLane: Lane));2262 const eventTime = requestEventTime();2263 const root = markUpdateLaneFromFiberToRoot(rootFiber, (SyncLane: Lane));2264 if (root !== null) {2265 markRootUpdated(root, SyncLane, eventTime);2266 ensureRootIsScheduled(root, eventTime);2267 }2268}2269export function captureCommitPhaseError(2270 sourceFiber: Fiber,2271 nearestMountedAncestor: Fiber | null,2272 error: mixed,2273) {2274 if (sourceFiber.tag === HostRoot) {2275 // Error was thrown at the root. There is no parent, so the root2276 // itself should capture it.2277 captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);2278 return;2279 }2280 let fiber = null;2281 if (skipUnmountedBoundaries) {2282 fiber = nearestMountedAncestor;2283 } else {2284 fiber = sourceFiber.return;2285 }2286 while (fiber !== null) {2287 if (fiber.tag === HostRoot) {2288 captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);2289 return;2290 } else if (fiber.tag === ClassComponent) {2291 const ctor = fiber.type;2292 const instance = fiber.stateNode;2293 if (2294 typeof ctor.getDerivedStateFromError === 'function' ||2295 (typeof instance.componentDidCatch === 'function' &&2296 !isAlreadyFailedLegacyErrorBoundary(instance))2297 ) {2298 const errorInfo = createCapturedValue(error, sourceFiber);2299 const update = createClassErrorUpdate(2300 fiber,2301 errorInfo,2302 (SyncLane: Lane),2303 );2304 enqueueUpdate(fiber, update, (SyncLane: Lane));2305 const eventTime = requestEventTime();2306 const root = markUpdateLaneFromFiberToRoot(fiber, (SyncLane: Lane));2307 if (root !== null) {2308 markRootUpdated(root, SyncLane, eventTime);2309 ensureRootIsScheduled(root, eventTime);2310 }2311 return;2312 }2313 }2314 fiber = fiber.return;2315 }2316 if (__DEV__) {2317 // TODO: Until we re-land skipUnmountedBoundaries (see #20147), this warning2318 // will fire for errors that are thrown by destroy functions inside deleted2319 // trees. What it should instead do is propagate the error to the parent of2320 // the deleted tree. In the meantime, do not add this warning to the2321 // allowlist; this is only for our internal use.2322 console.error(2323 'Internal React error: Attempted to capture a commit phase error ' +2324 'inside a detached tree. This indicates a bug in React. Likely ' +2325 'causes include deleting the same fiber more than once, committing an ' +2326 'already-finished tree, or an inconsistent return pointer.\n\n' +2327 'Error message:\n\n%s',2328 error,2329 );2330 }2331}2332export function pingSuspendedRoot(2333 root: FiberRoot,2334 wakeable: Wakeable,2335 pingedLanes: Lanes,2336) {2337 const pingCache = root.pingCache;2338 if (pingCache !== null) {2339 // The wakeable resolved, so we no longer need to memoize, because it will2340 // never be thrown again.2341 pingCache.delete(wakeable);2342 }2343 const eventTime = requestEventTime();2344 markRootPinged(root, pingedLanes, eventTime);2345 warnIfSuspenseResolutionNotWrappedWithActDEV(root);2346 if (2347 workInProgressRoot === root &&2348 isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)2349 ) {2350 // Received a ping at the same priority level at which we're currently2351 // rendering. We might want to restart this render. This should mirror2352 // the logic of whether or not a root suspends once it completes.2353 // TODO: If we're rendering sync either due to Sync, Batched or expired,2354 // we should probably never restart.2355 // If we're suspended with delay, or if it's a retry, we'll always suspend2356 // so we can always restart.2357 if (2358 workInProgressRootExitStatus === RootSuspendedWithDelay ||2359 (workInProgressRootExitStatus === RootSuspended &&2360 includesOnlyRetries(workInProgressRootRenderLanes) &&2361 now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS)2362 ) {2363 // Restart from the root.2364 prepareFreshStack(root, NoLanes);2365 } else {2366 // Even though we can't restart right now, we might get an2367 // opportunity later. So we mark this render as having a ping.2368 workInProgressRootPingedLanes = mergeLanes(2369 workInProgressRootPingedLanes,2370 pingedLanes,2371 );2372 }2373 }2374 ensureRootIsScheduled(root, eventTime);2375}2376function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {2377 // The boundary fiber (a Suspense component or SuspenseList component)2378 // previously was rendered in its fallback state. One of the promises that2379 // suspended it has resolved, which means at least part of the tree was2380 // likely unblocked. Try rendering again, at a new lanes.2381 if (retryLane === NoLane) {2382 // TODO: Assign this to `suspenseState.retryLane`? to avoid2383 // unnecessary entanglement?2384 retryLane = requestRetryLane(boundaryFiber);2385 }2386 // TODO: Special case idle priority?2387 const eventTime = requestEventTime();2388 const root = markUpdateLaneFromFiberToRoot(boundaryFiber, retryLane);2389 if (root !== null) {2390 markRootUpdated(root, retryLane, eventTime);2391 ensureRootIsScheduled(root, eventTime);2392 }2393}2394export function retryDehydratedSuspenseBoundary(boundaryFiber: Fiber) {2395 const suspenseState: null | SuspenseState = boundaryFiber.memoizedState;2396 let retryLane = NoLane;2397 if (suspenseState !== null) {2398 retryLane = suspenseState.retryLane;2399 }2400 retryTimedOutBoundary(boundaryFiber, retryLane);2401}2402export function resolveRetryWakeable(boundaryFiber: Fiber, wakeable: Wakeable) {2403 let retryLane = NoLane; // Default2404 let retryCache: WeakSet<Wakeable> | Set<Wakeable> | null;...
ReactFiberLane.js
Source:ReactFiberLane.js
1import {2 ImmediatePriority as ImmediateSchedulerPriority,3 UserBlockingPriority as UserBlockingSchedulerPriority,4 NormalPriority as NormalSchedulerPriority,5 LowPriority as LowSchedulerPriority,6 IdlePriority as IdleSchedulerPriority,7 NoPriority as NoSchedulerPriority,8} from './SchedulerWithReactIntegration';9const SyncLanePriority = 15;10const SyncBatchedLanePriority = 14;11const InputDiscreteHydrationLanePriority = 13;12const InputDiscreteLanePriority = 12;13const InputContinuousHydrationLanePriority = 11;14const InputContinuousLanePriority = 10;15const DefaultHydrationLanePriority = 9;16const DefaultLanePriority = 8;17const TransitionHydrationPriority = 7;18const TransitionPriority = 6;19const RetryLanePriority = 5;20const SelectiveHydrationLanePriority = 4;21const IdleHydrationLanePriority = 3;22const IdleLanePriority = 2;23const OffscreenLanePriority = 1;24const NoLanePriority = 0;25const createLaneMap = (initial) =>26 Array(31)27 .fill(0)28 .map(() => initial);29const NoLanes = 0b0000000000000000000000000000000;30const NoLane = 0b0000000000000000000000000000000;31const SyncLane = 0b0000000000000000000000000000001;32const SyncBatchedLane = 0b0000000000000000000000000000010;33const InputDiscreteHydrationLane = 0b0000000000000000000000000000100;34const InputDiscreteLanes = 0b0000000000000000000000000011000;35const InputContinuousHydrationLane = 0b0000000000000000000000000100000;36const InputContinuousLanes = 0b0000000000000000000000011000000;37const DefaultHydrationLane = 0b0000000000000000000000100000000;38const DefaultLanes = 0b0000000000000000000111000000000;39const TransitionHydrationLane = 0b0000000000000000001000000000000;40const TransitionLanes = 0b0000000001111111110000000000000;41const IdleHydrationLane = 0b0001000000000000000000000000000;42const IdleLanes = 0b0110000000000000000000000000000;43const NonIdleLanes = 0b0000111111111111111111111111111;44const OffscreenLane = 0b1000000000000000000000000000000;45const NoTimestamp = -1;46const getHighestPriorityLane = (lanes) => lanes & -lanes;47const pickArbitraryLane = (lanes) => getHighestPriorityLane(lanes);48const findUpdateLane = (lanePriority, wipLanes) => {49 let lane;50 switch (lanePriority) {51 case NoLanePriority:52 break;53 case SyncLanePriority:54 return SyncLane;55 case SyncBatchedLanePriority:56 return SyncBatchedLane;57 case InputDiscreteLanePriority: {58 lane = pickArbitraryLane(InputDiscreteLanes & ~wipLanes);59 if (lane === NoLane) {60 return findUpdateLane(InputContinuousLanePriority, wipLanes);61 }62 return lane;63 }64 case InputContinuousLanePriority: {65 lane = pickArbitraryLane(InputContinuousLanes & ~wipLanes);66 if (lane === NoLane) {67 return findUpdateLane(DefaultLanePriority, wipLanes);68 }69 return lane;70 }71 case DefaultLanePriority: {72 lane = pickArbitraryLane(DefaultLanes & ~wipLanes);73 if (lane === NoLane) {74 lane = pickArbitraryLane(TransitionLanes & ~wipLanes);75 if (lane === NoLane) {76 lane = pickArbitraryLane(DefaultLanes);77 }78 }79 return lane;80 }81 case TransitionPriority:82 case RetryLanePriority:83 break;84 case IdleLanePriority:85 lane = pickArbitraryLane(IdleLanes & ~wipLanes);86 if (lane === NoLane) {87 lane = pickArbitraryLane(IdleLanes);88 }89 return lane;90 default:91 break;92 }93 throw new Error('Invalid update priority: %s. This is a bug in React.');94};95const schedulerPriorityToLanePriority = (schedulerPriorityLevel) => {96 switch (schedulerPriorityLevel) {97 case ImmediateSchedulerPriority:98 return SyncLanePriority;99 case UserBlockingSchedulerPriority:100 return InputContinuousLanePriority;101 case NormalSchedulerPriority:102 case LowSchedulerPriority:103 return DefaultLanePriority;104 case IdleSchedulerPriority:105 return IdleLanePriority;106 default:107 return NoLanePriority;108 }109};110const isSubsetOfLanes = (set, subset) => (set & subset) === subset;111const mergeLanes = (a, b) => a | b;112const pickArbitraryLaneIndex = (lane) => 31 - Math.clz32(lane);113const markRootUpdated = (root, updateLane, eventTime) => {114 root.pendingLanes |= updateLane;115 const higherPriorityLanes = updateLane - 1;116 root.suspendedLanes &= higherPriorityLanes;117 root.pingedLanes &= higherPriorityLanes;118 const eventTimes = root.eventTimes;119 const index = pickArbitraryLaneIndex(updateLane);120 eventTimes[index] = eventTime;121};122const markRootSuspended = (root, suspendedLanes) => {123 root.suspendedLanes |= suspendedLanes;124 root.pingedLanes &= ~suspendedLanes;125 const expirationTimes = root.expirationTimes;126 let lanes = suspendedLanes;127 while (lanes > 0) {128 const index = pickArbitraryLaneIndex(lanes);129 const lane = 1 << index;130 expirationTimes[index] = NoTimestamp;131 lanes &= ~lane;132 }133};134const includesSomeLane = (a, b) => (a & b) !== NoLanes;135let return_highestLanePriority = DefaultLanePriority;136const getHighestPriorityLanes = (lanes) => {137 if ((SyncLane & lanes) !== NoLanes) {138 return_highestLanePriority = SyncLanePriority;139 return SyncLane;140 }141 if ((SyncBatchedLane & lanes) !== NoLanes) {142 return_highestLanePriority = SyncBatchedLanePriority;143 return SyncBatchedLane;144 }145 if ((InputDiscreteHydrationLane & lanes) !== NoLanes) {146 return_highestLanePriority = InputDiscreteHydrationLanePriority;147 return InputDiscreteHydrationLane;148 }149 const inputDiscreteLanes = InputDiscreteLanes & lanes;150 if (inputDiscreteLanes !== NoLanes) {151 return_highestLanePriority = InputDiscreteLanePriority;152 return inputDiscreteLanes;153 }154 if ((lanes & InputContinuousHydrationLane) !== NoLanes) {155 return_highestLanePriority = InputContinuousHydrationLanePriority;156 return InputContinuousHydrationLane;157 }158 const inputContinuousLanes = InputContinuousLanes & lanes;159 if (inputContinuousLanes !== NoLanes) {160 return_highestLanePriority = InputContinuousLanePriority;161 return inputContinuousLanes;162 }163 if ((lanes & DefaultHydrationLane) !== NoLanes) {164 return_highestLanePriority = DefaultHydrationLanePriority;165 return DefaultHydrationLane;166 }167 const defaultLanes = DefaultLanes & lanes;168 if (defaultLanes !== NoLanes) {169 return_highestLanePriority = DefaultLanePriority;170 return defaultLanes;171 }172 if ((lanes & TransitionHydrationLane) !== NoLanes) {173 return_highestLanePriority = TransitionHydrationPriority;174 return TransitionHydrationLane;175 }176 const transitionLanes = TransitionLanes & lanes;177 if (transitionLanes !== NoLanes) {178 return_highestLanePriority = TransitionPriority;179 return transitionLanes;180 }181 const retryLanes = RetryLanes & lanes;182 if (retryLanes !== NoLanes) {183 return_highestLanePriority = RetryLanePriority;184 return retryLanes;185 }186 if (lanes & SelectiveHydrationLane) {187 return_highestLanePriority = SelectiveHydrationLanePriority;188 return SelectiveHydrationLane;189 }190 if ((lanes & IdleHydrationLane) !== NoLanes) {191 return_highestLanePriority = IdleHydrationLanePriority;192 return IdleHydrationLane;193 }194 const idleLanes = IdleLanes & lanes;195 if (idleLanes !== NoLanes) {196 return_highestLanePriority = IdleLanePriority;197 return idleLanes;198 }199 if ((OffscreenLane & lanes) !== NoLanes) {200 return_highestLanePriority = OffscreenLanePriority;201 return OffscreenLane;202 }203 return_highestLanePriority = DefaultLanePriority;204 return lanes;205};206const getLowestPriorityLane = (lanes) => {207 const index = 31 - Math.clz32(lanes);208 return index < 0 ? NoLanes : 1 << index;209};210const getNextLanes = (root, wipLanes) => {211 const pendingLanes = root.pendingLanes;212 if (pendingLanes === NoLanes) {213 return_highestLanePriority = NoLanePriority;214 return NoLanes;215 }216 let nextLanes = NoLanes;217 let nextLanePriority = NoLanePriority;218 const expiredLanes = root.expiredLanes;219 const suspendedLanes = root.suspendedLanes;220 const pingedLanes = root.pingedLanes;221 if (expiredLanes !== NoLanes) {222 nextLanes = expiredLanes;223 nextLanePriority = return_highestLanePriority = SyncLanePriority;224 } else {225 const nonIdlePendingLanes = pendingLanes & NonIdleLanes;226 if (nonIdlePendingLanes !== NoLanes) {227 const nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes;228 if (nonIdleUnblockedLanes !== NoLanes) {229 nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes);230 nextLanePriority = return_highestLanePriority;231 } else {232 const nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes;233 if (nonIdlePingedLanes !== NoLanes) {234 nextLanes = getHighestPriorityLanes(nonIdlePingedLanes);235 nextLanePriority = return_highestLanePriority;236 }237 }238 } else {239 const unblockedLanes = pendingLanes & ~suspendedLanes;240 if (unblockedLanes !== NoLanes) {241 nextLanes = getHighestPriorityLanes(unblockedLanes);242 nextLanePriority = return_highestLanePriority;243 } else {244 if (pingedLanes !== NoLanes) {245 nextLanes = getHighestPriorityLanes(pingedLanes);246 nextLanePriority = return_highestLanePriority;247 }248 }249 }250 }251 if (nextLanes === NoLanes) {252 return NoLanes;253 }254 nextLanes = pendingLanes & ((getLowestPriorityLane(nextLanes) << 1) - 1);255 if (256 wipLanes !== NoLanes &&257 wipLanes !== nextLanes &&258 (wipLanes & suspendedLanes) === NoLanes259 ) {260 getHighestPriorityLanes(wipLanes);261 const wipLanePriority = return_highestLanePriority;262 if (nextLanePriority <= wipLanePriority) {263 return wipLanes;264 } else {265 return_highestLanePriority = nextLanePriority;266 }267 }268 const entangledLanes = root.entangledLanes;269 if (entangledLanes !== NoLanes) {270 const entanglements = root.entanglements;271 let lanes = nextLanes & entangledLanes;272 while (lanes > 0) {273 const index = pickArbitraryLaneIndex(lanes);274 const lane = 1 << index;275 nextLanes |= entanglements[index];276 lanes &= ~lane;277 }278 }279 return nextLanes;280};281const markRootFinished = (root, remainingLanes) => {282 const noLongerPendingLanes = root.pendingLanes & ~remainingLanes;283 root.pendingLanes = remainingLanes;284 root.suspendedLanes = 0;285 root.pingedLanes = 0;286 root.expiredLanes &= remainingLanes;287 root.mutableReadLanes &= remainingLanes;288 root.entangledLanes &= remainingLanes;289 const entanglements = root.entanglements;290 const eventTimes = root.eventTimes;291 const expirationTimes = root.expirationTimes;292 let lanes = noLongerPendingLanes;293 while (lanes > 0) {294 const index = pickArbitraryLaneIndex(lanes);295 const lane = 1 << index;296 entanglements[index] = NoLanes;297 eventTimes[index] = NoTimestamp;298 expirationTimes[index] = NoTimestamp;299 lanes &= ~lane;300 }301};302const hasDiscreteLanes = (lanes) => (lanes & InputDiscreteLanes) !== NoLanes;303const computeExpirationTime = (lane, currentTime) => {304 getHighestPriorityLanes(lane);305 const priority = return_highestLanePriority;306 if (priority >= InputContinuousLanePriority) {307 return currentTime + 250;308 } else if (priority >= TransitionPriority) {309 return currentTime + 5000;310 } else {311 return NoTimestamp;312 }313};314const markStarvedLanesAsExpired = (root, currentTime) => {315 const pendingLanes = root.pendingLanes;316 const suspendedLanes = root.suspendedLanes;317 const pingedLanes = root.pingedLanes;318 const expirationTimes = root.expirationTimes;319 let lanes = pendingLanes;320 while (lanes > 0) {321 const index = pickArbitraryLaneIndex(lanes);322 const lane = 1 << index;323 const expirationTime = expirationTimes[index];324 if (expirationTime === NoTimestamp) {325 if (326 (lane & suspendedLanes) === NoLanes ||327 (lane & pingedLanes) !== NoLanes328 ) {329 expirationTimes[index] = computeExpirationTime(lane, currentTime);330 }331 } else if (expirationTime <= currentTime) {332 root.expiredLanes |= lane;333 }334 lanes &= ~lane;335 }336};337const returnNextLanesPriority = () => return_highestLanePriority;338const lanePriorityToSchedulerPriority = (lanePriority) => {339 switch (lanePriority) {340 case SyncLanePriority:341 case SyncBatchedLanePriority:342 return ImmediateSchedulerPriority;343 case InputDiscreteHydrationLanePriority:344 case InputDiscreteLanePriority:345 case InputContinuousHydrationLanePriority:346 case InputContinuousLanePriority:347 return UserBlockingSchedulerPriority;348 case DefaultHydrationLanePriority:349 case DefaultLanePriority:350 case TransitionHydrationPriority:351 case TransitionPriority:352 case SelectiveHydrationLanePriority:353 case RetryLanePriority:354 return NormalSchedulerPriority;355 case IdleHydrationLanePriority:356 case IdleLanePriority:357 case OffscreenLanePriority:358 return IdleSchedulerPriority;359 case NoLanePriority:360 return NoSchedulerPriority;361 default:362 invariant(363 false,364 'Invalid update priority: %s. This is a bug in React.',365 lanePriority366 );367 }368};369export {370 SyncLanePriority,371 SyncBatchedLanePriority,372 InputDiscreteLanePriority,373 InputContinuousLanePriority,374 DefaultLanePriority,375 TransitionPriority,376 NoLanePriority,377 createLaneMap,378 NoLanes,379 NoLane,380 SyncLane,381 SyncBatchedLane,382 InputDiscreteHydrationLane,383 DefaultHydrationLane,384 DefaultLanes,385 IdleHydrationLane,386 OffscreenLane,387 NoTimestamp,388 pickArbitraryLane,389 findUpdateLane,390 schedulerPriorityToLanePriority,391 isSubsetOfLanes,392 mergeLanes,393 markRootUpdated,394 markRootSuspended,395 includesSomeLane,396 getNextLanes,397 markRootFinished,398 hasDiscreteLanes,399 markStarvedLanesAsExpired,400 returnNextLanesPriority,401 lanePriorityToSchedulerPriority,...
FiberWorkLoop.js
Source:FiberWorkLoop.js
...82}83export function scheduleUpdateOnFiber(fiber, lane, eventTime){84 const root = markUpdateLaneFromChildToRoot(fiber, lane);85 // update root.pendingLanes, eventTimes etc.86 markRootUpdated(root, lane, eventTime);87 ensureRootIsScheduled(root, eventTime);88 return root;89}90function markUpdateLaneFromChildToRoot(sourceFiber, lane){91 sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);92 let alternate = sourceFiber.alternate;93 if (alternate !== null){94 alternate.lanes = mergeLanes(alternate.lanes, lane);95 }96 let node = sourceFiber;97 let parent = sourceFiber.return;98 while(parent !== null){99 parent.childLanes = mergeLanes(parent.childLanes, lane);100 alternate = parent.alternate;101 if(alternate !== null){102 alternate.childLanes = mergeLanes(alternate.childLanes, lane);103 }104 node = parent;105 parent = parent.return;106 }107 if (node.tag === HostRoot){108 return node.stateNode109 } 110 return null;111}112function ensureRootIsScheduled(root, currentTime){113 // update root.expirationTime. 114 markStarvedLanesAsExpired(root, currentTime);115 const nextLanes = getNextLanes(116 root, 117 root === wipRoot ? wipRootRenderLanes : NoLanes,118 );119 if (nextLanes === NoLanes){120 return;121 }122 const newCallbackPriority = getHighestPriorityLane(nextLanes);123 // Reuse existing task with the same priority.124 const existingCallbackPriority = root.callbackPriority;125 if (existingCallbackPriority === newCallbackPriority){126 return;127 }128 let newCallbackNode = scheduleCallback(129 performConcurrentWorkOnRoot.bind(null, root),130 );131 root.callbackPriority = newCallbackPriority;132 root.callbackNode = newCallbackNode;133}134// Entry point for every concurrent task, i.e. anything that135// goes through Scheduler.136function performConcurrentWorkOnRoot(root){137 currentEventTime = NoTimestamp;138 const originalCallbackNode = root.callbackNode;139 let lanes = getNextLanes(140 root,141 root === wipRoot ? wipRootRenderLanes : NoLanes,142 )143 let exitStatus = renderRootConcurrent(root, lanes); 144 if(exitStatus !== RootIncomplete){145 if(exitStatus === RootErrored){146 executionContext |= RootErrored;147 return null;148 }149 // now we have a consistent tree and ready to commit.150 const finishedWork = root.current.alternate151 root.finishedWork = finishedWork;152 root.finishedLanes = lanes;153 finishConcurrentRender(root, exitStatus, lanes);154 }155 //schedule new tasks found in Render Phase156 ensureRootIsScheduled(root, performance.now());157 // root.callbackNode is always relevant to a task which hasn't completely 158 // finished due to expiration or some other reasons and it will be set to 159 // null in Commit Phase.160 if (root.callbackNode === originalCallbackNode){161 return performConcurrentWorkOnRoot.bind(null, root);162 }163 return null;164}165function finishConcurrentRender(root, exitStatus, lanes){166 switch (exitStatus){167 case RootCompleted:{168 commitRoot(root);169 break;170 }171 case RootSuspendedWithDelay:172 case RootSuspended:{ 173 markRootSuspended(root, lanes);174 // work expired. Commit immediately.175 commitRoot(root);176 break;177 }178 }179}180export function pushRenderLanes(fiber, lanes){181 push(subtreeRenderLanesCursor, subtreeRenderLanes);182 subtreeRenderLanes = mergeLanes(subtreeRenderLanes, lanes);183 wipRootIncludedLanes = mergeLanes(184 wipRootIncludedLanes,185 lanes,186 );187}188export function popRenderLanes(){189 subtreeRenderLanes = subtreeRenderLanesCursor.current;190 pop(subtreeRenderLanesCursor);191}192function prepareFreshStack(root, lanes){193 if (wip !== null){194 console.error('Leftover work found:', wip);195 }196 wipRoot = root;197 wip = createWorkInProgress(root.current);198 wipRootRenderLanes = subtreeRenderLanes = wipRootIncludedLanes = lanes;199 wipRootExitStatus = RootIncomplete;200 wipRootSkippedLanes = wipRootUpdatedLanes = wipRootPingedLanes = NoLanes;201}202function handleError(root, thrownValue){203 let erroredWork = wip;204 try {205 throwException(206 root,207 erroredWork.return,208 erroredWork,209 thrownValue,210 wipRootRenderLanes211 );212 completeUnitOfWork(erroredWork);213 } catch (yetAnotherThrownValue){214 console.error(yetAnotherThrownValue);215 }216}217export function markSkippedUpdateLanes(lane){218 wipRootSkippedLanes = mergeLanes(219 lane, 220 wipRootSkippedLanes,221 )222}223export function renderDidSuspend(){224 if(wipRootExitStatus === RootIncomplete){225 wipRootExitStatus = RootSuspended;226 }227}228export function renderDidSuspendDelayIfPossible(){229 if(230 wipRootExitStatus === RootIncomplete ||231 wipRootExitStatus === RootSuspended232 ){233 wipRootExitStatus = RootSuspendedWithDelay;234 }235 if(236 wipRoot !== null &&237 (includesNonIdleWork(wipRootSkippedLanes) ||238 includesNonIdleWork(wipRootUpdatedLanes))239 ){240 markRootSuspended(wipRoot, wipRootRenderLanes);241 }242}243export function renderDidError(){244 if (wipRootExitStatus !== RootCompleted){245 wipRootExitStatus = RootErrored;246 }247}248function renderRootConcurrent(root, lanes){249 const prevExecutionContext = executionContext;250 executionContext |= RenderContext;251 // If the root or lanes have changed, throw out the existing stack252 // and prepare a fresh one. Otherwise we'll continue where we left off.253 if (wipRoot !== root || wipRootRenderLanes !== lanes){254 //create a new FiberNode by cloning root.current and set it to wip.255 prepareFreshStack(root, lanes);256 }257 //Keep trying until all caught errors handled.258 do{259 try {260 workLoopConcurrent();261 break;262 } catch(thrownValue){263 handleError(root, thrownValue);264 }265 } while (true);266 267 executionContext = prevExecutionContext;268 if(wip !== null){269 return RootIncomplete;270 }271 wipRoot = null;272 wipRootRenderLanes = NoLanes273 return wipRootExitStatus;274}275function workLoopConcurrent(){276 // Perform work until Scheduler asks us to yield277 while(wip !== null && !shouldYieldToHost()){278 performUnitOfWork(wip);279 }280}281function performUnitOfWork(unitOfWork){282 const current = unitOfWork.alternate;283 let next = beginWork(current, unitOfWork, subtreeRenderLanes);284 unitOfWork.memoizedProps = unitOfWork.pendingProps;285 if (next === null){286 // If this doesn't spawn new work, complete the current work.287 completeUnitOfWork(unitOfWork);288 } else {289 wip = next;290 }291}292function completeUnitOfWork(unitOfWork){293 // Attempt to complete the current unit of work, then move to the next294 // sibling. If there are no more siblings, return to the parent fiber.295 let completedWork = unitOfWork;296 do {297 const current = completedWork.alternate;298 const returnFiber = completedWork.return;299 if ((completedWork.flags & Incomplete) === NoFlags){ 300 let next = completeWork(current, completedWork, subtreeRenderLanes);301 if (next !== null) {302 wip = next;303 return;304 }305 } else {306 // Error threw307 const next = unwindWork(completedWork, subtreeRenderLanes);308 if (next !== null){309 // Error fixed and return to normal render phase.310 next.flags &= HostEffectMask;311 wip = next;312 return;313 }314 if (returnFiber!==null){315 returnFiber.flags |= Incomplete;316 returnFiber.subtreeFlags = NoFlags;317 returnFiber.deletions = null;318 }319 }320 const siblingFiber = completedWork.sibling;321 if (siblingFiber !== null) {322 wip = siblingFiber;323 return;324 }325 completedWork = returnFiber;326 // when reached the root, returnFiber is null, set wip to null to make sure performUnitOfWork() in workLoopConcurrent() wont keep running.327 wip = completedWork;328 } while (completedWork !== null);329 // We've reached the root.330 if (wipRootExitStatus === RootIncomplete) {331 wipRootExitStatus = RootCompleted;332 }333}334function commitRoot(root){335 const finishedWork = root.finishedWork;336 const lanes = root.finishedLanes;337 root.finishedWork = null;338 root.finishedLanes = NoLanes;339 root.callbackNode = null;340 root.callbackPriority = NoLane;341 let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);342 markRootFinished(root, remainingLanes); 343 // schedule a callback to process pending passive effects.344 if (345 (finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||346 (finishedWork.flags & PassiveMask) !== NoFlags347 ){348 if(!rootDoesHavePassiveEffects){349 rootDoesHavePassiveEffects = true;350 scheduleCallback(()=>{351 flushPassiveEffects();352 return null;353 })354 }355 }356 const subtreeHasEffects = 357 (finishedWork.subtreeFlags &358 (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !== 359 NoFlags;360 const rootHasEffect = 361 (finishedWork.flags &362 (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==363 NoFlags;364 if (subtreeHasEffects || rootHasEffect){365 const prevExecutionContext= executionContext;366 executionContext |= CommitContext;367 commitBeforeMutationEffects(finishedWork);368 commitMutationEffects(root, finishedWork);369 commitLayoutEffects(finishedWork, root, lanes);370 371 executionContext = prevExecutionContext;372 } 373 root.current = finishedWork;374 375 const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;376 if (rootDoesHavePassiveEffects){377 rootDoesHavePassiveEffects = false;378 rootWithPendingPassiveEffects = root;379 pendingPassiveEffectsLanes = lanes;380 }381 ensureRootIsScheduled(root, performance.now())382}383function flushPassiveEffects(){384 if (pendingPassiveEffectsLanes !== NoLanes){385 flushPassiveEffectsImpl();386 }387 return false;388}389function flushPassiveEffectsImpl(){390 const root = rootWithPendingPassiveEffects;391 const lanes = pendingPassiveEffectsLanes;392 rootWithPendingPassiveEffects = null;393 pendingPassiveEffectsLanes = NoLanes;394 const prevExecutionContext = executionContext;395 executionContext |= CommitContext;396 commitPassiveUnmountEffects(root.current);397 commitPassiveMountEffects(root, root.current);398 executionContext = prevExecutionContext;399}400export function pingSuspendedRoot(root, wakeable, pingedLanes){401 // The earliest attach to catch the change from Promise. And to resolve 402 // Suspended Lanes before Commit Phase.403 const pingCache = root.pingCache;404 if (pingCache !== null){405 pingCache.delete(wakeable);406 }407 const eventTime = requestEventTime();408 ensureRootIsScheduled(root, eventTime);409}410function retryTimedOutBoundary(boundaryFiber, retryLane=NoLane){411 // The boundary fiber (Suspense) previously was rendered in its fallback 412 // state. One of the promises that suspended is has resolved and try 413 // rendering again at a new expiration time.414 if (retryLane === NoLane) {415 retryLane = claimNextRetryLane();416 }417 const eventTime = requestEventTime();418 const root = markUpdateLaneFromChildToRoot(boundaryFiber, retryLane);419 if (root !== null){420 markRootUpdated(root, retryLane, eventTime);421 ensureRootIsScheduled(root, eventTime);422 }423}424export function resolveRetryWakeable(boundaryFiber, wakeable){425 let retryCache = boundaryFiber.stateNode;426 if(retryCache !== null){427 retryCache.delete(wakeable);428 }429 retryTimedOutBoundary(boundaryFiber);...
legacy.js
Source:legacy.js
...111function scheduleUpdateOnFiber(fiber, lane, eventTime) {112 // æ è®°ä»Fiberå°æ ¹çæ´æ°éé113 const root = markUpdateLaneFromFiberToRoot(fiber, lane);114 // æ è®°æ ¹æä¸ä¸ªæèµ·çæ´æ°115 markRootUpdated(root, lane, eventTime);116 // åæ¥Lane legacyæ´æ°æ¨¡å¼117 if(lane === SyncLane) {118 // renderé¶æ®µ èµ·ç¹119 performSyncWorkOnRoot(root)120 }else {121 // concurrent Mode 122 ensureRootIsScheduled(root, eventTime);123 }...
FiberLane.js
Source:FiberLane.js
...123}124export function removeLanes(set, subset){125 return set & ~subset;126}127export function markRootUpdated(root, updateLane, eventTime){128 root.pendingLanes |= updateLane;129 130 const eventTimes = root.eventTimes;131 const index = laneToIndex(updateLane);132 eventTimes[index] = eventTime;133}134export function markRootSuspended(root, suspendedLanes){135 root.suspendedLanes |= suspendedLanes;136 root.pingedLanes &= ~suspendedLanes;137 // The suspended lanes are no longer CPU-bound. Clear the expiration times.138 const expirationTimes = root.expirationTimes;139 let lanes = suspendedLanes;140 while (lanes > 0){141 const index = laneToIndex(lanes);...
ReactFiberWorkLoop.js
Source:ReactFiberWorkLoop.js
...39 }40}41export function scheduleUpdateOnFiber(fiber, lane = SyncLane, eventTime) {42 // const root = markUpdateLaneFromFiberToRoot(fiber, lane);43 // markRootUpdated(root, lane, eventTime);44 if (lane === SyncLane) {45 if (46 // Check if we're inside unbatchedUpdates47 (executionContext & LegacyUnbatchedContext) !== NoContext &&48 // Check if we're not already rendering49 (executionContext & (RenderContext | CommitContext)) === NoContext50 ) {51 performSyncWorkOnRoot(fiber.stateNode);52 }53 }54}55// ä¸ç»è¿è°åº¦å¨ï¼åæ¥ä»»å¡çèµ·ç¹56// This is the entry point for synchronous tasks that don't go57// through Scheduler...
Using AI Code Generation
1const playwright = require('playwright');2(async () => {3 const browser = await playwright.chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 await page.$eval('#hplogo', el => el.remove());7 await page.evaluate(() => window.playwright.markRootUpdated());8 await page.screenshot({ path: 'example.png' });9 await browser.close();10})();
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.markRootUpdated();6 await browser.close();7})();8Page.markAsSecure()9const { chromium } = require('playwright');10(async () => {11 const browser = await chromium.launch();12 const page = await browser.newPage();13 await page.markAsSecure();14 await browser.close();15})();16Page.markAsAd()17const { chromium } = require('playwright');18(async () => {19 const browser = await chromium.launch();20 const page = await browser.newPage();21 await page.markAsAd();22 await browser.close();23})();24Page.setLifecycleEventsEnabled()25const { chromium } = require('playwright');26(async () => {27 const browser = await chromium.launch();28 const page = await browser.newPage();29 await page.setLifecycleEventsEnabled(true);30 await browser.close();31})();32Page.setFileChooserIntercepted()
Using AI Code Generation
1const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');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 markRootUpdated(page);8 await page.screenshot({ path: 'example.png' });9 await browser.close();10})();11const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');12const { chromium } = require('playwright');13(async () => {14 const browser = await chromium.launch();15 const context = await browser.newContext();16 const page = await context.newPage();17 await markRootUpdated(page);18 await page.screenshot({ path: 'example.png' });19 await browser.close();20})();21const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');22const { chromium } = require('playwright');23(async () => {24 const browser = await chromium.launch();25 const context = await browser.newContext();26 const page = await context.newPage();27 await markRootUpdated(page);28 await page.screenshot({ path: 'example.png' });29 await browser.close();30})();31const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');32const { chromium } = require('playwright');33(async () => {34 const browser = await chromium.launch();35 const context = await browser.newContext();36 const page = await context.newPage();37 await markRootUpdated(page);38 await page.screenshot({ path: 'example.png' });39 await browser.close();40})();41const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');42const { chromium } = require('playwright');43(async () => {44 const browser = await chromium.launch();45 const context = await browser.newContext();
Using AI Code Generation
1const { markRootUpdated } = require('playwright/lib/server/dom.js');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 const element = await page.$('input[name="q"]');8 await element.click();9 await page.keyboard.type('Hello World');10 await page.keyboard.press('Enter');11 await markRootUpdated(page);12 await page.screenshot({ path: 'google.png' });13 await browser.close();14})();15import { chromium } from 'playwright';16import { markRootUpdated } from 'playwright/lib/server/dom.js';17(async () => {18 const browser = await chromium.launch();19 const context = await browser.newContext();20 const page = await context.newPage();21 const element = await page.$('input[name="q"]');22 await element.click();23 await page.keyboard.type('Hello World');24 await page.keyboard.press('Enter');25 await markRootUpdated(page);26 await page.screenshot({ path: 'google.png' });27 await browser.close();28})();
Using AI Code Generation
1const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');2markRootUpdated(page);3const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');4markRootUpdated(page);5const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');6markRootUpdated(page);7const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');8markRootUpdated(page);9const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');10markRootUpdated(page);11const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');12markRootUpdated(page);13const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');14markRootUpdated(page);15const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');16markRootUpdated(page);17const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');18markRootUpdated(page);19const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');20markRootUpdated(page);21const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');22markRootUpdated(page);23const { markRootUpdated } = require('playwright/lib/server/chromium/crPage');24markRootUpdated(page);25const { markRootUpdated
Using AI Code Generation
1const { markRootUpdated } = require('playwright/lib/server/domSnapshot');2const { createSnapshot } = require('playwright/lib/server/domSnapshot');3const { createDocument } = require('playwright/lib/server/domSnapshot');4const { createNode } = require('playwright/lib/server/domSnapshot');5const { createAttribute } = require('playwright/lib/server/domSnapshot');6const { createText } = require('playwright/lib/server/domSnapshot');7const { createComment } = require('playwright/lib/server/domSnapshot');8const document = createDocument();9const div = createNode('div', null, []);10const text = createText('text');11const comment = createComment('comment');12const attr = createAttribute('attr', 'value');13div.childNodes = [text, comment];14div.attributes = [attr];15document.root = div;16const snapshot = createSnapshot(document);17markRootUpdated(snapshot);18console.log(JSON.stringify(snapshot));19const { markRootUpdated } = require('playwright/lib/server/domSnapshot');20const { createSnapshot } = require('playwright/lib/server/domSnapshot');21const { createDocument } = require('playwright/lib/server/domSnapshot');22const { createNode } = require('playwright/lib/server/domSnapshot');23const { createAttribute } = require('playwright/lib/server/domSnapshot');24const { createText } = require('playwright/lib/server/domSnapshot');25const { createComment } = require('playwright/lib/server/domSnapshot');26const document = createDocument();27const div = createNode('div', null, []);28const text = createText('text');29const comment = createComment('comment');30const attr = createAttribute('attr', 'value');31div.childNodes = [text, comment];32div.attributes = [attr];33document.root = div;34const snapshot = createSnapshot(document);35markRootUpdated(snapshot);36console.log(JSON.stringify(snapshot));37const { markRootUpdated } = require('playwright/lib/server/domSnapshot');38const { createSnapshot } = require('playwright/lib/server/domSnapshot');39const { createDocument } = require('playwright/lib/server/domSnapshot');40const { createNode } = require('playwright/lib/server/domSnapshot');41const { createAttribute } = require('playwright/lib/server/domSnapshot');42const { createText } = require('playwright/lib/server/domSnapshot');43const { createComment } = require('playwright/lib/server/domSnapshot');
Using AI Code Generation
1import { markRootUpdated } from "playwright/lib/server/inspector/inspector.js";2const playwright = require("playwright");3(async () => {4 const browser = await playwright["chromium"].launch();5 const context = await browser.newContext();6 const page = await context.newPage();7 await page.screenshot({ path: "example.png" });8 await browser.close();9})();
Using AI Code Generation
1playwright._internal.markRootUpdated(document.body)2playwright._internal.markRootUpdated(document.body)3playwright._internal.markRootUpdated(document.body)4playwright._internal.markRootUpdated(document.body)5playwright._internal.markRootUpdated(document.body)6playwright._internal.markRootUpdated(document.body)7playwright._internal.markRootUpdated(document.body)8playwright._internal.markRootUpdated(document.body)9playwright._internal.markRootUpdated(document.body)10playwright._internal.markRootUpdated(document.body)11playwright._internal.markRootUpdated(document.body)12playwright._internal.markRootUpdated(document.body)
Using AI Code Generation
1import { markRootUpdated } from '@playwright/internal';2markRootUpdated();3TypeError: (0, _playwright_internal.markRootUpdated) is not a function4I have imported the markRootUpdated method from the playwright/internal module. I don’t understand why I am getting this error. Can anyone help me with this?5@Shubham_Yadav, you need to import the module first. Like this:6import { markRootUpdated } from '@playwright/internal';7@Shubham_Yadav, you need to import the module first. Like this:8import { markRootUpdated } from '@playwright/internal';9@Shubham_Yadav, I see. I think you need to use the following import:10import { markRootUpdated } from '@playwright/test/lib/utils';11@Shubham_Yadav, I see. I think you need to use the following import:12import { markRootUpdated } from '@playwright/test/lib/utils';13import { markRootUpdated } from '@playwright/test/lib/utils';14markRootUpdated();15const { test } = require('@playwright/test');16test('my test', async ({ page }) => {17 try {18 await page.waitForSelector('not_existing_selector');19 } catch (e) {20 const { markRootUpdated } = require('@playwright/test/lib/utils');21 markRootUpdated();22 throw e;23 }24});
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!!