How to use warnAboutUpdateOnUnmountedFiberInDEV method in Playwright Internal

Best JavaScript code snippet using playwright-internal

ReactFiberWorkLoop.new.js

Source:ReactFiberWorkLoop.new.js Github

copy

Full Screen

...484 checkForNestedUpdates();485 warnAboutRenderPhaseUpdatesInDEV(fiber);486 const root = markUpdateLaneFromFiberToRoot(fiber, lane);487 if (root === null) {488 warnAboutUpdateOnUnmountedFiberInDEV(fiber);489 return null;490 }491 // Mark that the root has a pending update.492 markRootUpdated(root, lane, eventTime);493 if (root === workInProgressRoot) {494 // Received an update to a tree that's in the middle of rendering. Mark495 // that there was an interleaved update work on this root. Unless the496 // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render497 // phase update. In that case, we don't treat render phase updates as if498 // they were interleaved, for backwards compat reasons.499 if (500 deferRenderPhaseUpdateToNextBatch ||501 (executionContext & RenderContext) === NoContext502 ) {503 workInProgressRootUpdatedLanes = mergeLanes(504 workInProgressRootUpdatedLanes,505 lane,506 );507 }508 if (workInProgressRootExitStatus === RootSuspendedWithDelay) {509 // The root already suspended with a delay, which means this render510 // definitely won't finish. Since we have a new update, let's mark it as511 // suspended now, right before marking the incoming update. This has the512 // effect of interrupting the current render and switching to the update.513 // TODO: Make sure this doesn't override pings that happen while we've514 // already started rendering.515 markRootSuspended(root, workInProgressRootRenderLanes);516 }517 }518 // TODO: requestUpdateLanePriority also reads the priority. Pass the519 // priority as an argument to that function and this one.520 const priorityLevel = getCurrentPriorityLevel();521 if (lane === SyncLane) {522 if (523 // Check if we're inside unbatchedUpdates524 (executionContext & LegacyUnbatchedContext) !== NoContext &&525 // Check if we're not already rendering526 (executionContext & (RenderContext | CommitContext)) === NoContext527 ) {528 // Register pending interactions on the root to avoid losing traced interaction data.529 schedulePendingInteractions(root, lane);530 // This is a legacy edge case. The initial mount of a ReactDOM.render-ed531 // root inside of batchedUpdates should be synchronous, but layout updates532 // should be deferred until the end of the batch.533 performSyncWorkOnRoot(root);534 } else {535 ensureRootIsScheduled(root, eventTime);536 schedulePendingInteractions(root, lane);537 if (executionContext === NoContext) {538 // Flush the synchronous work now, unless we're already working or inside539 // a batch. This is intentionally inside scheduleUpdateOnFiber instead of540 // scheduleCallbackForFiber to preserve the ability to schedule a callback541 // without immediately flushing it. We only do this for user-initiated542 // updates, to preserve historical behavior of legacy mode.543 resetRenderTimer();544 flushSyncCallbackQueue();545 }546 }547 } else {548 // Schedule a discrete update but only if it's not Sync.549 if (550 (executionContext & DiscreteEventContext) !== NoContext &&551 // Only updates at user-blocking priority or greater are considered552 // discrete, even inside a discrete event.553 (priorityLevel === UserBlockingSchedulerPriority ||554 priorityLevel === ImmediateSchedulerPriority)555 ) {556 // This is the result of a discrete event. Track the lowest priority557 // discrete update per root so we can flush them early, if needed.558 if (rootsWithPendingDiscreteUpdates === null) {559 rootsWithPendingDiscreteUpdates = new Set([root]);560 } else {561 rootsWithPendingDiscreteUpdates.add(root);562 }563 }564 // Schedule other updates after in case the callback is sync.565 ensureRootIsScheduled(root, eventTime);566 schedulePendingInteractions(root, lane);567 }568 // We use this when assigning a lane for a transition inside569 // `requestUpdateLane`. We assume it's the same as the root being updated,570 // since in the common case of a single root app it probably is. If it's not571 // the same root, then it's not a huge deal, we just might batch more stuff572 // together more than necessary.573 mostRecentlyUpdatedRoot = root;574}575// This is split into a separate function so we can mark a fiber with pending576// work without treating it as a typical update that originates from an event;577// e.g. retrying a Suspense boundary isn't an update, but it does schedule work578// on a fiber.579function markUpdateLaneFromFiberToRoot(580 sourceFiber: Fiber,581 lane: Lane,582): FiberRoot | null {583 // Update the source fiber's lanes584 sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);585 let alternate = sourceFiber.alternate;586 if (alternate !== null) {587 alternate.lanes = mergeLanes(alternate.lanes, lane);588 }589 if (__DEV__) {590 if (591 alternate === null &&592 (sourceFiber.flags & (Placement | Hydrating)) !== NoFlags593 ) {594 warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);595 }596 }597 // Walk the parent path to the root and update the child expiration time.598 let node = sourceFiber;599 let parent = sourceFiber.return;600 while (parent !== null) {601 parent.childLanes = mergeLanes(parent.childLanes, lane);602 alternate = parent.alternate;603 if (alternate !== null) {604 alternate.childLanes = mergeLanes(alternate.childLanes, lane);605 } else {606 if (__DEV__) {607 if ((parent.flags & (Placement | Hydrating)) !== NoFlags) {608 warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);609 }610 }611 }612 node = parent;613 parent = parent.return;614 }615 if (node.tag === HostRoot) {616 const root: FiberRoot = node.stateNode;617 return root;618 } else {619 return null;620 }621}622// Use this function to schedule a task for a root. There's only one task per623// root; if a task was already scheduled, we'll check to make sure the priority624// of the existing task is the same as the priority of the next level that the625// root has work on. This function is called on every update, and right before626// exiting a task.627function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {628 const existingCallbackNode = root.callbackNode;629 // Check if any lanes are being starved by other work. If so, mark them as630 // expired so we know to work on those next.631 markStarvedLanesAsExpired(root, currentTime);632 // Determine the next lanes to work on, and their priority.633 const nextLanes = getNextLanes(634 root,635 root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,636 );637 // This returns the priority level computed during the `getNextLanes` call.638 const newCallbackPriority = returnNextLanesPriority();639 if (nextLanes === NoLanes) {640 // Special case: There's nothing to work on.641 if (existingCallbackNode !== null) {642 cancelCallback(existingCallbackNode);643 root.callbackNode = null;644 root.callbackPriority = NoLanePriority;645 }646 return;647 }648 // Check if there's an existing task. We may be able to reuse it.649 if (existingCallbackNode !== null) {650 const existingCallbackPriority = root.callbackPriority;651 if (existingCallbackPriority === newCallbackPriority) {652 // The priority hasn't changed. We can reuse the existing task. Exit.653 return;654 }655 // The priority changed. Cancel the existing callback. We'll schedule a new656 // one below.657 cancelCallback(existingCallbackNode);658 }659 // Schedule a new callback.660 let newCallbackNode;661 if (newCallbackPriority === SyncLanePriority) {662 // Special case: Sync React callbacks are scheduled on a special663 // internal queue664 newCallbackNode = scheduleSyncCallback(665 performSyncWorkOnRoot.bind(null, root),666 );667 } else if (newCallbackPriority === SyncBatchedLanePriority) {668 newCallbackNode = scheduleCallback(669 ImmediateSchedulerPriority,670 performSyncWorkOnRoot.bind(null, root),671 );672 } else {673 const schedulerPriorityLevel = lanePriorityToSchedulerPriority(674 newCallbackPriority,675 );676 newCallbackNode = scheduleCallback(677 schedulerPriorityLevel,678 performConcurrentWorkOnRoot.bind(null, root),679 );680 }681 root.callbackPriority = newCallbackPriority;682 root.callbackNode = newCallbackNode;683}684// This is the entry point for every concurrent task, i.e. anything that685// goes through Scheduler.686function performConcurrentWorkOnRoot(root) {687 // Since we know we're in a React event, we can clear the current688 // event time. The next update will compute a new event time.689 currentEventTime = NoTimestamp;690 currentEventWipLanes = NoLanes;691 currentEventPendingLanes = NoLanes;692 invariant(693 (executionContext & (RenderContext | CommitContext)) === NoContext,694 'Should not already be working.',695 );696 // Flush any pending passive effects before deciding which lanes to work on,697 // in case they schedule additional work.698 const originalCallbackNode = root.callbackNode;699 const didFlushPassiveEffects = flushPassiveEffects();700 if (didFlushPassiveEffects) {701 // Something in the passive effect phase may have canceled the current task.702 // Check if the task node for this root was changed.703 if (root.callbackNode !== originalCallbackNode) {704 // The current task was canceled. Exit. We don't need to call705 // `ensureRootIsScheduled` because the check above implies either that706 // there's a new task, or that there's no remaining work on this root.707 return null;708 } else {709 // Current task was not canceled. Continue.710 }711 }712 // Determine the next expiration time to work on, using the fields stored713 // on the root.714 let lanes = getNextLanes(715 root,716 root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,717 );718 if (lanes === NoLanes) {719 // Defensive coding. This is never expected to happen.720 return null;721 }722 let exitStatus = renderRootConcurrent(root, lanes);723 if (724 includesSomeLane(725 workInProgressRootIncludedLanes,726 workInProgressRootUpdatedLanes,727 )728 ) {729 // The render included lanes that were updated during the render phase.730 // For example, when unhiding a hidden tree, we include all the lanes731 // that were previously skipped when the tree was hidden. That set of732 // lanes is a superset of the lanes we started rendering with.733 //734 // So we'll throw out the current work and restart.735 prepareFreshStack(root, NoLanes);736 } else if (exitStatus !== RootIncomplete) {737 if (exitStatus === RootErrored) {738 executionContext |= RetryAfterError;739 // If an error occurred during hydration,740 // discard server response and fall back to client side render.741 if (root.hydrate) {742 root.hydrate = false;743 clearContainer(root.containerInfo);744 }745 // If something threw an error, try rendering one more time. We'll render746 // synchronously to block concurrent data mutations, and we'll includes747 // all pending updates are included. If it still fails after the second748 // attempt, we'll give up and commit the resulting tree.749 lanes = getLanesToRetrySynchronouslyOnError(root);750 if (lanes !== NoLanes) {751 exitStatus = renderRootSync(root, lanes);752 }753 }754 if (exitStatus === RootFatalErrored) {755 const fatalError = workInProgressRootFatalError;756 prepareFreshStack(root, NoLanes);757 markRootSuspended(root, lanes);758 ensureRootIsScheduled(root, now());759 throw fatalError;760 }761 // We now have a consistent tree. The next step is either to commit it,762 // or, if something suspended, wait to commit it after a timeout.763 const finishedWork: Fiber = (root.current.alternate: any);764 root.finishedWork = finishedWork;765 root.finishedLanes = lanes;766 finishConcurrentRender(root, exitStatus, lanes);767 }768 ensureRootIsScheduled(root, now());769 if (root.callbackNode === originalCallbackNode) {770 // The task node scheduled for this root is the same one that's771 // currently executed. Need to return a continuation.772 return performConcurrentWorkOnRoot.bind(null, root);773 }774 return null;775}776function finishConcurrentRender(root, exitStatus, lanes) {777 switch (exitStatus) {778 case RootIncomplete:779 case RootFatalErrored: {780 invariant(false, 'Root did not complete. This is a bug in React.');781 }782 // Flow knows about invariant, so it complains if I add a break783 // statement, but eslint doesn't know about invariant, so it complains784 // if I do. eslint-disable-next-line no-fallthrough785 case RootErrored: {786 // We should have already attempted to retry this tree. If we reached787 // this point, it errored again. Commit it.788 commitRoot(root);789 break;790 }791 case RootSuspended: {792 markRootSuspended(root, lanes);793 // We have an acceptable loading state. We need to figure out if we794 // should immediately commit it or wait a bit.795 if (796 includesOnlyRetries(lanes) &&797 // do not delay if we're inside an act() scope798 !shouldForceFlushFallbacksInDEV()799 ) {800 // This render only included retries, no updates. Throttle committing801 // retries so that we don't show too many loading states too quickly.802 const msUntilTimeout =803 globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now();804 // Don't bother with a very short suspense time.805 if (msUntilTimeout > 10) {806 const nextLanes = getNextLanes(root, NoLanes);807 if (nextLanes !== NoLanes) {808 // There's additional work on this root.809 break;810 }811 const suspendedLanes = root.suspendedLanes;812 if (!isSubsetOfLanes(suspendedLanes, lanes)) {813 // We should prefer to render the fallback of at the last814 // suspended level. Ping the last suspended level to try815 // rendering it again.816 // FIXME: What if the suspended lanes are Idle? Should not restart.817 const eventTime = requestEventTime();818 markRootPinged(root, suspendedLanes, eventTime);819 break;820 }821 // The render is suspended, it hasn't timed out, and there's no822 // lower priority work to do. Instead of committing the fallback823 // immediately, wait for more data to arrive.824 root.timeoutHandle = scheduleTimeout(825 commitRoot.bind(null, root),826 msUntilTimeout,827 );828 break;829 }830 }831 // The work expired. Commit immediately.832 commitRoot(root);833 break;834 }835 case RootSuspendedWithDelay: {836 markRootSuspended(root, lanes);837 if (includesOnlyTransitions(lanes)) {838 // This is a transition, so we should exit without committing a839 // placeholder and without scheduling a timeout. Delay indefinitely840 // until we receive more data.841 break;842 }843 if (!shouldForceFlushFallbacksInDEV()) {844 // This is not a transition, but we did trigger an avoided state.845 // Schedule a placeholder to display after a short delay, using the Just846 // Noticeable Difference.847 // TODO: Is the JND optimization worth the added complexity? If this is848 // the only reason we track the event time, then probably not.849 // Consider removing.850 const mostRecentEventTime = getMostRecentEventTime(root, lanes);851 const eventTimeMs = mostRecentEventTime;852 const timeElapsedMs = now() - eventTimeMs;853 const msUntilTimeout = jnd(timeElapsedMs) - timeElapsedMs;854 // Don't bother with a very short suspense time.855 if (msUntilTimeout > 10) {856 // Instead of committing the fallback immediately, wait for more data857 // to arrive.858 root.timeoutHandle = scheduleTimeout(859 commitRoot.bind(null, root),860 msUntilTimeout,861 );862 break;863 }864 }865 // Commit the placeholder.866 commitRoot(root);867 break;868 }869 case RootCompleted: {870 // The work completed. Ready to commit.871 commitRoot(root);872 break;873 }874 default: {875 invariant(false, 'Unknown root exit status.');876 }877 }878}879function markRootSuspended(root, suspendedLanes) {880 // When suspending, we should always exclude lanes that were pinged or (more881 // rarely, since we try to avoid it) updated during the render phase.882 // TODO: Lol maybe there's a better way to factor this besides this883 // obnoxiously named function :)884 suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);885 suspendedLanes = removeLanes(suspendedLanes, workInProgressRootUpdatedLanes);886 markRootSuspended_dontCallThisOneDirectly(root, suspendedLanes);887}888// This is the entry point for synchronous tasks that don't go889// through Scheduler890function performSyncWorkOnRoot(root) {891 invariant(892 (executionContext & (RenderContext | CommitContext)) === NoContext,893 'Should not already be working.',894 );895 flushPassiveEffects();896 let lanes;897 let exitStatus;898 if (899 root === workInProgressRoot &&900 includesSomeLane(root.expiredLanes, workInProgressRootRenderLanes)901 ) {902 // There's a partial tree, and at least one of its lanes has expired. Finish903 // rendering it before rendering the rest of the expired work.904 lanes = workInProgressRootRenderLanes;905 exitStatus = renderRootSync(root, lanes);906 if (907 includesSomeLane(908 workInProgressRootIncludedLanes,909 workInProgressRootUpdatedLanes,910 )911 ) {912 // The render included lanes that were updated during the render phase.913 // For example, when unhiding a hidden tree, we include all the lanes914 // that were previously skipped when the tree was hidden. That set of915 // lanes is a superset of the lanes we started rendering with.916 //917 // Note that this only happens when part of the tree is rendered918 // concurrently. If the whole tree is rendered synchronously, then there919 // are no interleaved events.920 lanes = getNextLanes(root, lanes);921 exitStatus = renderRootSync(root, lanes);922 }923 } else {924 lanes = getNextLanes(root, NoLanes);925 exitStatus = renderRootSync(root, lanes);926 }927 if (root.tag !== LegacyRoot && exitStatus === RootErrored) {928 executionContext |= RetryAfterError;929 // If an error occurred during hydration,930 // discard server response and fall back to client side render.931 if (root.hydrate) {932 root.hydrate = false;933 clearContainer(root.containerInfo);934 }935 // If something threw an error, try rendering one more time. We'll render936 // synchronously to block concurrent data mutations, and we'll includes937 // all pending updates are included. If it still fails after the second938 // attempt, we'll give up and commit the resulting tree.939 lanes = getLanesToRetrySynchronouslyOnError(root);940 if (lanes !== NoLanes) {941 exitStatus = renderRootSync(root, lanes);942 }943 }944 if (exitStatus === RootFatalErrored) {945 const fatalError = workInProgressRootFatalError;946 prepareFreshStack(root, NoLanes);947 markRootSuspended(root, lanes);948 ensureRootIsScheduled(root, now());949 throw fatalError;950 }951 // We now have a consistent tree. Because this is a sync render, we952 // will commit it even if something suspended.953 const finishedWork: Fiber = (root.current.alternate: any);954 root.finishedWork = finishedWork;955 root.finishedLanes = lanes;956 commitRoot(root);957 // Before exiting, make sure there's a callback scheduled for the next958 // pending level.959 ensureRootIsScheduled(root, now());960 return null;961}962export function flushRoot(root: FiberRoot, lanes: Lanes) {963 markRootExpired(root, lanes);964 ensureRootIsScheduled(root, now());965 if ((executionContext & (RenderContext | CommitContext)) === NoContext) {966 resetRenderTimer();967 flushSyncCallbackQueue();968 }969}970export function getExecutionContext(): ExecutionContext {971 return executionContext;972}973export function flushDiscreteUpdates() {974 // TODO: Should be able to flush inside batchedUpdates, but not inside `act`.975 // However, `act` uses `batchedUpdates`, so there's no way to distinguish976 // those two cases. Need to fix this before exposing flushDiscreteUpdates977 // as a public API.978 if (979 (executionContext & (BatchedContext | RenderContext | CommitContext)) !==980 NoContext981 ) {982 if (__DEV__) {983 if ((executionContext & RenderContext) !== NoContext) {984 console.error(985 'unstable_flushDiscreteUpdates: Cannot flush updates when React is ' +986 'already rendering.',987 );988 }989 }990 // We're already rendering, so we can't synchronously flush pending work.991 // This is probably a nested event dispatch triggered by a lifecycle/effect,992 // like `el.focus()`. Exit.993 return;994 }995 flushPendingDiscreteUpdates();996 // If the discrete updates scheduled passive effects, flush them now so that997 // they fire before the next serial event.998 flushPassiveEffects();999}1000export function deferredUpdates<A>(fn: () => A): A {1001 if (decoupleUpdatePriorityFromScheduler) {1002 const previousLanePriority = getCurrentUpdateLanePriority();1003 try {1004 setCurrentUpdateLanePriority(DefaultLanePriority);1005 return runWithPriority(NormalSchedulerPriority, fn);1006 } finally {1007 setCurrentUpdateLanePriority(previousLanePriority);1008 }1009 } else {1010 return runWithPriority(NormalSchedulerPriority, fn);1011 }1012}1013function flushPendingDiscreteUpdates() {1014 if (rootsWithPendingDiscreteUpdates !== null) {1015 // For each root with pending discrete updates, schedule a callback to1016 // immediately flush them.1017 const roots = rootsWithPendingDiscreteUpdates;1018 rootsWithPendingDiscreteUpdates = null;1019 roots.forEach(root => {1020 markDiscreteUpdatesExpired(root);1021 ensureRootIsScheduled(root, now());1022 });1023 }1024 // Now flush the immediate queue.1025 flushSyncCallbackQueue();1026}1027export function batchedUpdates<A, R>(fn: A => R, a: A): R {1028 const prevExecutionContext = executionContext;1029 executionContext |= BatchedContext;1030 try {1031 return fn(a);1032 } finally {1033 executionContext = prevExecutionContext;1034 if (executionContext === NoContext) {1035 // Flush the immediate callbacks that were scheduled during this batch1036 resetRenderTimer();1037 flushSyncCallbackQueue();1038 }1039 }1040}1041export function batchedEventUpdates<A, R>(fn: A => R, a: A): R {1042 const prevExecutionContext = executionContext;1043 executionContext |= EventContext;1044 try {1045 return fn(a);1046 } finally {1047 executionContext = prevExecutionContext;1048 if (executionContext === NoContext) {1049 // Flush the immediate callbacks that were scheduled during this batch1050 resetRenderTimer();1051 flushSyncCallbackQueue();1052 }1053 }1054}1055export function discreteUpdates<A, B, C, D, R>(1056 fn: (A, B, C) => R,1057 a: A,1058 b: B,1059 c: C,1060 d: D,1061): R {1062 const prevExecutionContext = executionContext;1063 executionContext |= DiscreteEventContext;1064 if (decoupleUpdatePriorityFromScheduler) {1065 const previousLanePriority = getCurrentUpdateLanePriority();1066 try {1067 setCurrentUpdateLanePriority(InputDiscreteLanePriority);1068 return runWithPriority(1069 UserBlockingSchedulerPriority,1070 fn.bind(null, a, b, c, d),1071 );1072 } finally {1073 setCurrentUpdateLanePriority(previousLanePriority);1074 executionContext = prevExecutionContext;1075 if (executionContext === NoContext) {1076 // Flush the immediate callbacks that were scheduled during this batch1077 resetRenderTimer();1078 flushSyncCallbackQueue();1079 }1080 }1081 } else {1082 try {1083 return runWithPriority(1084 UserBlockingSchedulerPriority,1085 fn.bind(null, a, b, c, d),1086 );1087 } finally {1088 executionContext = prevExecutionContext;1089 if (executionContext === NoContext) {1090 // Flush the immediate callbacks that were scheduled during this batch1091 resetRenderTimer();1092 flushSyncCallbackQueue();1093 }1094 }1095 }1096}1097export function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {1098 const prevExecutionContext = executionContext;1099 executionContext &= ~BatchedContext;1100 executionContext |= LegacyUnbatchedContext;1101 try {1102 return fn(a);1103 } finally {1104 executionContext = prevExecutionContext;1105 if (executionContext === NoContext) {1106 // Flush the immediate callbacks that were scheduled during this batch1107 resetRenderTimer();1108 flushSyncCallbackQueue();1109 }1110 }1111}1112export function flushSync<A, R>(fn: A => R, a: A): R {1113 const prevExecutionContext = executionContext;1114 if ((prevExecutionContext & (RenderContext | CommitContext)) !== NoContext) {1115 if (__DEV__) {1116 console.error(1117 'flushSync was called from inside a lifecycle method. React cannot ' +1118 'flush when React is already rendering. Consider moving this call to ' +1119 'a scheduler task or micro task.',1120 );1121 }1122 return fn(a);1123 }1124 executionContext |= BatchedContext;1125 if (decoupleUpdatePriorityFromScheduler) {1126 const previousLanePriority = getCurrentUpdateLanePriority();1127 try {1128 setCurrentUpdateLanePriority(SyncLanePriority);1129 if (fn) {1130 return runWithPriority(ImmediateSchedulerPriority, fn.bind(null, a));1131 } else {1132 return (undefined: $FlowFixMe);1133 }1134 } finally {1135 setCurrentUpdateLanePriority(previousLanePriority);1136 executionContext = prevExecutionContext;1137 // Flush the immediate callbacks that were scheduled during this batch.1138 // Note that this will happen even if batchedUpdates is higher up1139 // the stack.1140 flushSyncCallbackQueue();1141 }1142 } else {1143 try {1144 if (fn) {1145 return runWithPriority(ImmediateSchedulerPriority, fn.bind(null, a));1146 } else {1147 return (undefined: $FlowFixMe);1148 }1149 } finally {1150 executionContext = prevExecutionContext;1151 // Flush the immediate callbacks that were scheduled during this batch.1152 // Note that this will happen even if batchedUpdates is higher up1153 // the stack.1154 flushSyncCallbackQueue();1155 }1156 }1157}1158export function flushControlled(fn: () => mixed): void {1159 const prevExecutionContext = executionContext;1160 executionContext |= BatchedContext;1161 if (decoupleUpdatePriorityFromScheduler) {1162 const previousLanePriority = getCurrentUpdateLanePriority();1163 try {1164 setCurrentUpdateLanePriority(SyncLanePriority);1165 runWithPriority(ImmediateSchedulerPriority, fn);1166 } finally {1167 setCurrentUpdateLanePriority(previousLanePriority);1168 executionContext = prevExecutionContext;1169 if (executionContext === NoContext) {1170 // Flush the immediate callbacks that were scheduled during this batch1171 resetRenderTimer();1172 flushSyncCallbackQueue();1173 }1174 }1175 } else {1176 try {1177 runWithPriority(ImmediateSchedulerPriority, fn);1178 } finally {1179 executionContext = prevExecutionContext;1180 if (executionContext === NoContext) {1181 // Flush the immediate callbacks that were scheduled during this batch1182 resetRenderTimer();1183 flushSyncCallbackQueue();1184 }1185 }1186 }1187}1188export function pushRenderLanes(fiber: Fiber, lanes: Lanes) {1189 pushToStack(subtreeRenderLanesCursor, subtreeRenderLanes, fiber);1190 subtreeRenderLanes = mergeLanes(subtreeRenderLanes, lanes);1191 workInProgressRootIncludedLanes = mergeLanes(1192 workInProgressRootIncludedLanes,1193 lanes,1194 );1195}1196export function popRenderLanes(fiber: Fiber) {1197 subtreeRenderLanes = subtreeRenderLanesCursor.current;1198 popFromStack(subtreeRenderLanesCursor, fiber);1199}1200function prepareFreshStack(root: FiberRoot, lanes: Lanes) {1201 root.finishedWork = null;1202 root.finishedLanes = NoLanes;1203 const timeoutHandle = root.timeoutHandle;1204 if (timeoutHandle !== noTimeout) {1205 // The root previous suspended and scheduled a timeout to commit a fallback1206 // state. Now that we have additional work, cancel the timeout.1207 root.timeoutHandle = noTimeout;1208 // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above1209 cancelTimeout(timeoutHandle);1210 }1211 if (workInProgress !== null) {1212 let interruptedWork = workInProgress.return;1213 while (interruptedWork !== null) {1214 unwindInterruptedWork(interruptedWork);1215 interruptedWork = interruptedWork.return;1216 }1217 }1218 workInProgressRoot = root;1219 workInProgress = createWorkInProgress(root.current, null);1220 workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;1221 workInProgressRootExitStatus = RootIncomplete;1222 workInProgressRootFatalError = null;1223 workInProgressRootSkippedLanes = NoLanes;1224 workInProgressRootUpdatedLanes = NoLanes;1225 workInProgressRootPingedLanes = NoLanes;1226 if (enableSchedulerTracing) {1227 spawnedWorkDuringRender = null;1228 }1229 if (__DEV__) {1230 ReactStrictModeWarnings.discardPendingWarnings();1231 }1232}1233function handleError(root, thrownValue): void {1234 do {1235 let erroredWork = workInProgress;1236 try {1237 // Reset module-level state that was set during the render phase.1238 resetContextDependencies();1239 resetHooksAfterThrow();1240 resetCurrentDebugFiberInDEV();1241 // TODO: I found and added this missing line while investigating a1242 // separate issue. Write a regression test using string refs.1243 ReactCurrentOwner.current = null;1244 if (erroredWork === null || erroredWork.return === null) {1245 // Expected to be working on a non-root fiber. This is a fatal error1246 // because there's no ancestor that can handle it; the root is1247 // supposed to capture all errors that weren't caught by an error1248 // boundary.1249 workInProgressRootExitStatus = RootFatalErrored;1250 workInProgressRootFatalError = thrownValue;1251 // Set `workInProgress` to null. This represents advancing to the next1252 // sibling, or the parent if there are no siblings. But since the root1253 // has no siblings nor a parent, we set it to null. Usually this is1254 // handled by `completeUnitOfWork` or `unwindWork`, but since we're1255 // intentionally not calling those, we need set it here.1256 // TODO: Consider calling `unwindWork` to pop the contexts.1257 workInProgress = null;1258 return;1259 }1260 if (enableProfilerTimer && erroredWork.mode & ProfileMode) {1261 // Record the time spent rendering before an error was thrown. This1262 // avoids inaccurate Profiler durations in the case of a1263 // suspended render.1264 stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true);1265 }1266 throwException(1267 root,1268 erroredWork.return,1269 erroredWork,1270 thrownValue,1271 workInProgressRootRenderLanes,1272 );1273 completeUnitOfWork(erroredWork);1274 } catch (yetAnotherThrownValue) {1275 // Something in the return path also threw.1276 thrownValue = yetAnotherThrownValue;1277 if (workInProgress === erroredWork && erroredWork !== null) {1278 // If this boundary has already errored, then we had trouble processing1279 // the error. Bubble it to the next boundary.1280 erroredWork = erroredWork.return;1281 workInProgress = erroredWork;1282 } else {1283 erroredWork = workInProgress;1284 }1285 continue;1286 }1287 // Return to the normal work loop.1288 return;1289 } while (true);1290}1291function pushDispatcher() {1292 const prevDispatcher = ReactCurrentDispatcher.current;1293 ReactCurrentDispatcher.current = ContextOnlyDispatcher;1294 if (prevDispatcher === null) {1295 // The React isomorphic package does not include a default dispatcher.1296 // Instead the first renderer will lazily attach one, in order to give1297 // nicer error messages.1298 return ContextOnlyDispatcher;1299 } else {1300 return prevDispatcher;1301 }1302}1303function popDispatcher(prevDispatcher) {1304 ReactCurrentDispatcher.current = prevDispatcher;1305}1306function pushInteractions(root) {1307 if (enableSchedulerTracing) {1308 const prevInteractions: Set<Interaction> | null = __interactionsRef.current;1309 __interactionsRef.current = root.memoizedInteractions;1310 return prevInteractions;1311 }1312 return null;1313}1314function popInteractions(prevInteractions) {1315 if (enableSchedulerTracing) {1316 __interactionsRef.current = prevInteractions;1317 }1318}1319export function markCommitTimeOfFallback() {1320 globalMostRecentFallbackTime = now();1321}1322export function markSkippedUpdateLanes(lane: Lane | Lanes): void {1323 workInProgressRootSkippedLanes = mergeLanes(1324 lane,1325 workInProgressRootSkippedLanes,1326 );1327}1328export function renderDidSuspend(): void {1329 if (workInProgressRootExitStatus === RootIncomplete) {1330 workInProgressRootExitStatus = RootSuspended;1331 }1332}1333export function renderDidSuspendDelayIfPossible(): void {1334 if (1335 workInProgressRootExitStatus === RootIncomplete ||1336 workInProgressRootExitStatus === RootSuspended1337 ) {1338 workInProgressRootExitStatus = RootSuspendedWithDelay;1339 }1340 // Check if there are updates that we skipped tree that might have unblocked1341 // this render.1342 if (1343 workInProgressRoot !== null &&1344 (includesNonIdleWork(workInProgressRootSkippedLanes) ||1345 includesNonIdleWork(workInProgressRootUpdatedLanes))1346 ) {1347 // Mark the current render as suspended so that we switch to working on1348 // the updates that were skipped. Usually we only suspend at the end of1349 // the render phase.1350 // TODO: We should probably always mark the root as suspended immediately1351 // (inside this function), since by suspending at the end of the render1352 // phase introduces a potential mistake where we suspend lanes that were1353 // pinged or updated while we were rendering.1354 markRootSuspended(workInProgressRoot, workInProgressRootRenderLanes);1355 }1356}1357export function renderDidError() {1358 if (workInProgressRootExitStatus !== RootCompleted) {1359 workInProgressRootExitStatus = RootErrored;1360 }1361}1362// Called during render to determine if anything has suspended.1363// Returns false if we're not sure.1364export function renderHasNotSuspendedYet(): boolean {1365 // If something errored or completed, we can't really be sure,1366 // so those are false.1367 return workInProgressRootExitStatus === RootIncomplete;1368}1369function renderRootSync(root: FiberRoot, lanes: Lanes) {1370 const prevExecutionContext = executionContext;1371 executionContext |= RenderContext;1372 const prevDispatcher = pushDispatcher();1373 // If the root or lanes have changed, throw out the existing stack1374 // and prepare a fresh one. Otherwise we'll continue where we left off.1375 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1376 prepareFreshStack(root, lanes);1377 startWorkOnPendingInteractions(root, lanes);1378 }1379 const prevInteractions = pushInteractions(root);1380 if (__DEV__) {1381 if (enableDebugTracing) {1382 logRenderStarted(lanes);1383 }1384 }1385 if (enableSchedulingProfiler) {1386 markRenderStarted(lanes);1387 }1388 do {1389 try {1390 workLoopSync();1391 break;1392 } catch (thrownValue) {1393 handleError(root, thrownValue);1394 }1395 } while (true);1396 resetContextDependencies();1397 if (enableSchedulerTracing) {1398 popInteractions(((prevInteractions: any): Set<Interaction>));1399 }1400 executionContext = prevExecutionContext;1401 popDispatcher(prevDispatcher);1402 if (workInProgress !== null) {1403 // This is a sync render, so we should have finished the whole tree.1404 invariant(1405 false,1406 'Cannot commit an incomplete root. This error is likely caused by a ' +1407 'bug in React. Please file an issue.',1408 );1409 }1410 if (__DEV__) {1411 if (enableDebugTracing) {1412 logRenderStopped();1413 }1414 }1415 if (enableSchedulingProfiler) {1416 markRenderStopped();1417 }1418 // Set this to null to indicate there's no in-progress render.1419 workInProgressRoot = null;1420 workInProgressRootRenderLanes = NoLanes;1421 return workInProgressRootExitStatus;1422}1423// The work loop is an extremely hot path. Tell Closure not to inline it.1424/** @noinline */1425function workLoopSync() {1426 // Already timed out, so perform work without checking if we need to yield.1427 while (workInProgress !== null) {1428 performUnitOfWork(workInProgress);1429 }1430}1431function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {1432 const prevExecutionContext = executionContext;1433 executionContext |= RenderContext;1434 const prevDispatcher = pushDispatcher();1435 // If the root or lanes have changed, throw out the existing stack1436 // and prepare a fresh one. Otherwise we'll continue where we left off.1437 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1438 resetRenderTimer();1439 prepareFreshStack(root, lanes);1440 startWorkOnPendingInteractions(root, lanes);1441 }1442 const prevInteractions = pushInteractions(root);1443 if (__DEV__) {1444 if (enableDebugTracing) {1445 logRenderStarted(lanes);1446 }1447 }1448 if (enableSchedulingProfiler) {1449 markRenderStarted(lanes);1450 }1451 do {1452 try {1453 workLoopConcurrent();1454 break;1455 } catch (thrownValue) {1456 handleError(root, thrownValue);1457 }1458 } while (true);1459 resetContextDependencies();1460 if (enableSchedulerTracing) {1461 popInteractions(((prevInteractions: any): Set<Interaction>));1462 }1463 popDispatcher(prevDispatcher);1464 executionContext = prevExecutionContext;1465 if (__DEV__) {1466 if (enableDebugTracing) {1467 logRenderStopped();1468 }1469 }1470 // Check if the tree has completed.1471 if (workInProgress !== null) {1472 // Still work remaining.1473 if (enableSchedulingProfiler) {1474 markRenderYielded();1475 }1476 return RootIncomplete;1477 } else {1478 // Completed the tree.1479 if (enableSchedulingProfiler) {1480 markRenderStopped();1481 }1482 // Set this to null to indicate there's no in-progress render.1483 workInProgressRoot = null;1484 workInProgressRootRenderLanes = NoLanes;1485 // Return the final exit status.1486 return workInProgressRootExitStatus;1487 }1488}1489/** @noinline */1490function workLoopConcurrent() {1491 // Perform work until Scheduler asks us to yield1492 while (workInProgress !== null && !shouldYield()) {1493 performUnitOfWork(workInProgress);1494 }1495}1496function performUnitOfWork(unitOfWork: Fiber): void {1497 // The current, flushed, state of this fiber is the alternate. Ideally1498 // nothing should rely on this, but relying on it here means that we don't1499 // need an additional field on the work in progress.1500 const current = unitOfWork.alternate;1501 setCurrentDebugFiberInDEV(unitOfWork);1502 let next;1503 if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {1504 startProfilerTimer(unitOfWork);1505 next = beginWork(current, unitOfWork, subtreeRenderLanes);1506 stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);1507 } else {1508 next = beginWork(current, unitOfWork, subtreeRenderLanes);1509 }1510 resetCurrentDebugFiberInDEV();1511 unitOfWork.memoizedProps = unitOfWork.pendingProps;1512 if (next === null) {1513 // If this doesn't spawn new work, complete the current work.1514 completeUnitOfWork(unitOfWork);1515 } else {1516 workInProgress = next;1517 }1518 ReactCurrentOwner.current = null;1519}1520function completeUnitOfWork(unitOfWork: Fiber): void {1521 // Attempt to complete the current unit of work, then move to the next1522 // sibling. If there are no more siblings, return to the parent fiber.1523 let completedWork = unitOfWork;1524 do {1525 // The current, flushed, state of this fiber is the alternate. Ideally1526 // nothing should rely on this, but relying on it here means that we don't1527 // need an additional field on the work in progress.1528 const current = completedWork.alternate;1529 const returnFiber = completedWork.return;1530 // Check if the work completed or if something threw.1531 if ((completedWork.flags & Incomplete) === NoFlags) {1532 setCurrentDebugFiberInDEV(completedWork);1533 let next;1534 if (1535 !enableProfilerTimer ||1536 (completedWork.mode & ProfileMode) === NoMode1537 ) {1538 next = completeWork(current, completedWork, subtreeRenderLanes);1539 } else {1540 startProfilerTimer(completedWork);1541 next = completeWork(current, completedWork, subtreeRenderLanes);1542 // Update render duration assuming we didn't error.1543 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1544 }1545 resetCurrentDebugFiberInDEV();1546 if (next !== null) {1547 // Completing this fiber spawned new work. Work on that next.1548 workInProgress = next;1549 return;1550 }1551 } else {1552 // This fiber did not complete because something threw. Pop values off1553 // the stack without entering the complete phase. If this is a boundary,1554 // capture values if possible.1555 const next = unwindWork(completedWork, subtreeRenderLanes);1556 // Because this fiber did not complete, don't reset its expiration time.1557 if (next !== null) {1558 // If completing this work spawned new work, do that next. We'll come1559 // back here again.1560 // Since we're restarting, remove anything that is not a host effect1561 // from the effect tag.1562 next.flags &= HostEffectMask;1563 workInProgress = next;1564 return;1565 }1566 if (1567 enableProfilerTimer &&1568 (completedWork.mode & ProfileMode) !== NoMode1569 ) {1570 // Record the render duration for the fiber that errored.1571 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1572 // Include the time spent working on failed children before continuing.1573 let actualDuration = completedWork.actualDuration;1574 let child = completedWork.child;1575 while (child !== null) {1576 actualDuration += child.actualDuration;1577 child = child.sibling;1578 }1579 completedWork.actualDuration = actualDuration;1580 }1581 if (returnFiber !== null) {1582 // Mark the parent fiber as incomplete1583 returnFiber.flags |= Incomplete;1584 returnFiber.subtreeFlags = NoFlags;1585 returnFiber.deletions = null;1586 }1587 }1588 const siblingFiber = completedWork.sibling;1589 if (siblingFiber !== null) {1590 // If there is more work to do in this returnFiber, do that next.1591 workInProgress = siblingFiber;1592 return;1593 }1594 // Otherwise, return to the parent1595 completedWork = returnFiber;1596 // Update the next thing we're working on in case something throws.1597 workInProgress = completedWork;1598 } while (completedWork !== null);1599 // We've reached the root.1600 if (workInProgressRootExitStatus === RootIncomplete) {1601 workInProgressRootExitStatus = RootCompleted;1602 }1603}1604function commitRoot(root) {1605 const renderPriorityLevel = getCurrentPriorityLevel();1606 runWithPriority(1607 ImmediateSchedulerPriority,1608 commitRootImpl.bind(null, root, renderPriorityLevel),1609 );1610 return null;1611}1612function commitRootImpl(root, renderPriorityLevel) {1613 do {1614 // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which1615 // means `flushPassiveEffects` will sometimes result in additional1616 // passive effects. So we need to keep flushing in a loop until there are1617 // no more pending effects.1618 // TODO: Might be better if `flushPassiveEffects` did not automatically1619 // flush synchronous work at the end, to avoid factoring hazards like this.1620 flushPassiveEffects();1621 } while (rootWithPendingPassiveEffects !== null);1622 flushRenderPhaseStrictModeWarningsInDEV();1623 invariant(1624 (executionContext & (RenderContext | CommitContext)) === NoContext,1625 'Should not already be working.',1626 );1627 const finishedWork = root.finishedWork;1628 const lanes = root.finishedLanes;1629 if (__DEV__) {1630 if (enableDebugTracing) {1631 logCommitStarted(lanes);1632 }1633 }1634 if (enableSchedulingProfiler) {1635 markCommitStarted(lanes);1636 }1637 // 没有需要更新的Fiber节点1638 if (finishedWork === null) {1639 if (__DEV__) {1640 if (enableDebugTracing) {1641 logCommitStopped();1642 }1643 }1644 if (enableSchedulingProfiler) {1645 markCommitStopped();1646 }1647 return null;1648 }1649 root.finishedWork = null;1650 root.finishedLanes = NoLanes;1651 invariant(1652 finishedWork !== root.current,1653 'Cannot commit the same tree as before. This error is likely caused by ' +1654 'a bug in React. Please file an issue.',1655 );1656 // commitRoot never returns a continuation; it always finishes synchronously.1657 // So we can clear these now to allow a new callback to be scheduled.1658 root.callbackNode = null;1659 // Update the first and last pending times on this root. The new first1660 // pending time is whatever is left on the root fiber.1661 // 合并所有的通道1662 let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);1663 markRootFinished(root, remainingLanes);1664 // Clear already finished discrete updates in case that a later call of1665 // `flushDiscreteUpdates` starts a useless render pass which may cancels1666 // a scheduled timeout.1667 if (rootsWithPendingDiscreteUpdates !== null) {1668 if (1669 !hasDiscreteLanes(remainingLanes) &&1670 rootsWithPendingDiscreteUpdates.has(root)1671 ) {1672 rootsWithPendingDiscreteUpdates.delete(root);1673 }1674 }1675 // 说明本次更新已经完成了,将一些变量重置1676 if (root === workInProgressRoot) {1677 // We can reset these now that they are finished.1678 workInProgressRoot = null;1679 workInProgress = null;1680 workInProgressRootRenderLanes = NoLanes;1681 } else {1682 // This indicates that the last root we worked on is not the same one that1683 // we're committing now. This most commonly happens when a suspended root1684 // times out.1685 }1686 // Check if there are any effects in the whole tree.1687 // TODO: This is left over from the effect list implementation, where we had1688 // to check for the existence of `firstEffect` to satsify Flow. I think the1689 // only other reason this optimization exists is because it affects profiling.1690 // Reconsider whether this is necessary.1691 const subtreeHasEffects =1692 (finishedWork.subtreeFlags &1693 (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1694 NoFlags;1695 const rootHasEffect =1696 (finishedWork.flags &1697 (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1698 NoFlags;1699 // 有需要处理的副作用1700 if (subtreeHasEffects || rootHasEffect) {1701 let previousLanePriority;1702 if (decoupleUpdatePriorityFromScheduler) {1703 previousLanePriority = getCurrentUpdateLanePriority();1704 setCurrentUpdateLanePriority(SyncLanePriority);1705 }1706 // 设置执行上下文1707 const prevExecutionContext = executionContext;1708 executionContext |= CommitContext;1709 const prevInteractions = pushInteractions(root);1710 // Reset this to null before calling lifecycles1711 ReactCurrentOwner.current = null;1712 // The commit phase is broken into several sub-phases. We do a separate pass1713 // of the effect list for each phase: all mutation effects come before all1714 // layout effects, and so on.1715 // The first phase a "before mutation" phase. We use this phase to read the1716 // state of the host tree right before we mutate it. This is where1717 // getSnapshotBeforeUpdate is called.1718 focusedInstanceHandle = prepareForCommit(root.containerInfo);1719 shouldFireAfterActiveInstanceBlur = false;1720 commitBeforeMutationEffects(finishedWork);1721 // We no longer need to track the active instance fiber1722 focusedInstanceHandle = null;1723 if (enableProfilerTimer) {1724 // Mark the current commit time to be shared by all Profilers in this1725 // batch. This enables them to be grouped later.1726 recordCommitTime();1727 }1728 // 把DOM元素渲染到页面上1729 // The next phase is the mutation phase, where we mutate the host tree.1730 commitMutationEffects(finishedWork, root, renderPriorityLevel);1731 if (shouldFireAfterActiveInstanceBlur) {1732 afterActiveInstanceBlur();1733 }1734 resetAfterCommit(root.containerInfo);1735 // The work-in-progress tree is now the current tree. This must come after1736 // the mutation phase, so that the previous tree is still current during1737 // componentWillUnmount, but before the layout phase, so that the finished1738 // work is current during componentDidMount/Update.1739 // 在此之后,root.current 指向了work-in-progress tree1740 root.current = finishedWork;1741 // The next phase is the layout phase, where we call effects that read1742 // the host tree after it's been mutated. The idiomatic use case for this is1743 // layout, but class component lifecycles also fire here for legacy reasons.1744 if (__DEV__) {1745 if (enableDebugTracing) {1746 logLayoutEffectsStarted(lanes);1747 }1748 }1749 if (enableSchedulingProfiler) {1750 markLayoutEffectsStarted(lanes);1751 }1752 if (__DEV__) {1753 setCurrentDebugFiberInDEV(finishedWork);1754 invokeGuardedCallback(1755 null,1756 recursivelyCommitLayoutEffects,1757 null,1758 finishedWork,1759 root,1760 );1761 if (hasCaughtError()) {1762 const error = clearCaughtError();1763 captureCommitPhaseErrorOnRoot(finishedWork, finishedWork, error);1764 }1765 resetCurrentDebugFiberInDEV();1766 } else {1767 try {1768 recursivelyCommitLayoutEffects(finishedWork, root);1769 } catch (error) {1770 captureCommitPhaseErrorOnRoot(finishedWork, finishedWork, error);1771 }1772 }1773 if (__DEV__) {1774 if (enableDebugTracing) {1775 logLayoutEffectsStopped();1776 }1777 }1778 if (enableSchedulingProfiler) {1779 markLayoutEffectsStopped();1780 }1781 // If there are pending passive effects, schedule a callback to process them.1782 if (1783 (finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||1784 (finishedWork.flags & PassiveMask) !== NoFlags1785 ) {1786 if (!rootDoesHavePassiveEffects) {1787 rootDoesHavePassiveEffects = true;1788 scheduleCallback(NormalSchedulerPriority, () => {1789 flushPassiveEffects();1790 return null;1791 });1792 }1793 }1794 // Tell Scheduler to yield at the end of the frame, so the browser has an1795 // opportunity to paint.1796 requestPaint();1797 if (enableSchedulerTracing) {1798 popInteractions(((prevInteractions: any): Set<Interaction>));1799 }1800 executionContext = prevExecutionContext;1801 if (decoupleUpdatePriorityFromScheduler && previousLanePriority != null) {1802 // Reset the priority to the previous non-sync value.1803 setCurrentUpdateLanePriority(previousLanePriority);1804 }1805 } else {1806 // No effects.1807 root.current = finishedWork;1808 // Measure these anyway so the flamegraph explicitly shows that there were1809 // no effects.1810 // TODO: Maybe there's a better way to report this.1811 if (enableProfilerTimer) {1812 recordCommitTime();1813 }1814 }1815 const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;1816 if (rootDoesHavePassiveEffects) {1817 // This commit has passive effects. Stash a reference to them. But don't1818 // schedule a callback until after flushing layout work.1819 rootDoesHavePassiveEffects = false;1820 rootWithPendingPassiveEffects = root;1821 pendingPassiveEffectsLanes = lanes;1822 pendingPassiveEffectsRenderPriority = renderPriorityLevel;1823 }1824 // Read this again, since an effect might have updated it1825 remainingLanes = root.pendingLanes;1826 // Check if there's remaining work on this root1827 if (remainingLanes !== NoLanes) {1828 if (enableSchedulerTracing) {1829 if (spawnedWorkDuringRender !== null) {1830 const expirationTimes = spawnedWorkDuringRender;1831 spawnedWorkDuringRender = null;1832 for (let i = 0; i < expirationTimes.length; i++) {1833 scheduleInteractions(1834 root,1835 expirationTimes[i],1836 root.memoizedInteractions,1837 );1838 }1839 }1840 schedulePendingInteractions(root, remainingLanes);1841 }1842 } else {1843 // If there's no remaining work, we can clear the set of already failed1844 // error boundaries.1845 legacyErrorBoundariesThatAlreadyFailed = null;1846 }1847 if (__DEV__ && enableDoubleInvokingEffects) {1848 if (!rootDidHavePassiveEffects) {1849 commitDoubleInvokeEffectsInDEV(root.current, false);1850 }1851 }1852 if (enableSchedulerTracing) {1853 if (!rootDidHavePassiveEffects) {1854 // If there are no passive effects, then we can complete the pending interactions.1855 // Otherwise, we'll wait until after the passive effects are flushed.1856 // Wait to do this until after remaining work has been scheduled,1857 // so that we don't prematurely signal complete for interactions when there's e.g. hidden work.1858 finishPendingInteractions(root, lanes);1859 }1860 }1861 if (remainingLanes === SyncLane) {1862 // Count the number of times the root synchronously re-renders without1863 // finishing. If there are too many, it indicates an infinite update loop.1864 if (root === rootWithNestedUpdates) {1865 nestedUpdateCount++;1866 } else {1867 nestedUpdateCount = 0;1868 rootWithNestedUpdates = root;1869 }1870 } else {1871 nestedUpdateCount = 0;1872 }1873 onCommitRootDevTools(finishedWork.stateNode, renderPriorityLevel);1874 if (__DEV__) {1875 onCommitRootTestSelector();1876 }1877 // Always call this before exiting `commitRoot`, to ensure that any1878 // additional work on this root is scheduled.1879 ensureRootIsScheduled(root, now());1880 if (hasUncaughtError) {1881 hasUncaughtError = false;1882 const error = firstUncaughtError;1883 firstUncaughtError = null;1884 throw error;1885 }1886 if ((executionContext & LegacyUnbatchedContext) !== NoContext) {1887 if (__DEV__) {1888 if (enableDebugTracing) {1889 logCommitStopped();1890 }1891 }1892 if (enableSchedulingProfiler) {1893 markCommitStopped();1894 }1895 // This is a legacy edge case. We just committed the initial mount of1896 // a ReactDOM.render-ed root inside of batchedUpdates. The commit fired1897 // synchronously, but layout updates should be deferred until the end1898 // of the batch.1899 return null;1900 }1901 // If layout work was scheduled, flush it now.1902 flushSyncCallbackQueue();1903 if (__DEV__) {1904 if (enableDebugTracing) {1905 logCommitStopped();1906 }1907 }1908 if (enableSchedulingProfiler) {1909 markCommitStopped();1910 }1911 return null;1912}1913function commitBeforeMutationEffects(firstChild: Fiber) {1914 let fiber = firstChild;1915 while (fiber !== null) {1916 // 处理需要删除的fiber autoFocus、blur 逻辑。1917 if (fiber.deletions !== null) {1918 commitBeforeMutationEffectsDeletions(fiber.deletions);1919 }1920 // 递归调用处理子节点1921 if (fiber.child !== null) {1922 const primarySubtreeFlags = fiber.subtreeFlags & BeforeMutationMask;1923 if (primarySubtreeFlags !== NoFlags) {1924 commitBeforeMutationEffects(fiber.child);1925 }1926 }1927 if (__DEV__) {1928 setCurrentDebugFiberInDEV(fiber);1929 invokeGuardedCallback(null, commitBeforeMutationEffectsImpl, null, fiber);1930 if (hasCaughtError()) {1931 const error = clearCaughtError();1932 captureCommitPhaseError(fiber, fiber.return, error);1933 }1934 resetCurrentDebugFiberInDEV();1935 } else {1936 try {1937 // 调用 getSnapshotBeforeUpdate 生命周期1938 // 异步调度useEffect1939 commitBeforeMutationEffectsImpl(fiber);1940 } catch (error) {1941 captureCommitPhaseError(fiber, fiber.return, error);1942 }1943 }1944 // 返回兄弟节点,接着循环1945 fiber = fiber.sibling;1946 }1947}1948function commitBeforeMutationEffectsImpl(fiber: Fiber) {1949 const current = fiber.alternate;1950 const flags = fiber.flags;1951 if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {1952 // Check to see if the focused element was inside of a hidden (Suspense) subtree.1953 // TODO: Move this out of the hot path using a dedicated effect tag.1954 if (1955 fiber.tag === SuspenseComponent &&1956 isSuspenseBoundaryBeingHidden(current, fiber) &&1957 doesFiberContain(fiber, focusedInstanceHandle)1958 ) {1959 shouldFireAfterActiveInstanceBlur = true;1960 beforeActiveInstanceBlur();1961 }1962 }1963 if ((flags & Snapshot) !== NoFlags) {1964 setCurrentDebugFiberInDEV(fiber);1965 // 调用 getSnapshotBeforeUpdate 生命周期1966 commitBeforeMutationEffectOnFiber(current, fiber);1967 resetCurrentDebugFiberInDEV();1968 }1969 // 调度useEffect1970 if ((flags & Passive) !== NoFlags) {1971 // If there are passive effects, schedule a callback to flush at1972 // the earliest opportunity.1973 if (!rootDoesHavePassiveEffects) {1974 rootDoesHavePassiveEffects = true;1975 scheduleCallback(NormalSchedulerPriority, () => {1976 flushPassiveEffects();1977 return null;1978 });1979 }1980 }1981}1982function commitBeforeMutationEffectsDeletions(deletions: Array<Fiber>) {1983 for (let i = 0; i < deletions.length; i++) {1984 const fiber = deletions[i];1985 // TODO (effects) It would be nice to avoid calling doesFiberContain()1986 // Maybe we can repurpose one of the subtreeFlags positions for this instead?1987 // Use it to store which part of the tree the focused instance is in?1988 // This assumes we can safely determine that instance during the "render" phase.1989 if (doesFiberContain(fiber, ((focusedInstanceHandle: any): Fiber))) {1990 shouldFireAfterActiveInstanceBlur = true;1991 beforeActiveInstanceBlur();1992 }1993 }1994}1995function commitMutationEffects(1996 firstChild: Fiber,1997 root: FiberRoot,1998 renderPriorityLevel: ReactPriorityLevel,1999) {2000 let fiber = firstChild;2001 while (fiber !== null) {2002 const deletions = fiber.deletions;2003 if (deletions !== null) {2004 commitMutationEffectsDeletions(2005 deletions,2006 fiber,2007 root,2008 renderPriorityLevel,2009 );2010 }2011 if (fiber.child !== null) {2012 const mutationFlags = fiber.subtreeFlags & MutationMask;2013 if (mutationFlags !== NoFlags) {2014 commitMutationEffects(fiber.child, root, renderPriorityLevel);2015 }2016 }2017 if (__DEV__) {2018 setCurrentDebugFiberInDEV(fiber);2019 invokeGuardedCallback(2020 null,2021 commitMutationEffectsImpl,2022 null,2023 fiber,2024 root,2025 renderPriorityLevel,2026 );2027 if (hasCaughtError()) {2028 const error = clearCaughtError();2029 captureCommitPhaseError(fiber, fiber.return, error);2030 }2031 resetCurrentDebugFiberInDEV();2032 } else {2033 try {2034 commitMutationEffectsImpl(fiber, root, renderPriorityLevel);2035 } catch (error) {2036 captureCommitPhaseError(fiber, fiber.return, error);2037 }2038 }2039 fiber = fiber.sibling;2040 }2041}2042function commitMutationEffectsImpl(2043 fiber: Fiber,2044 root: FiberRoot,2045 renderPriorityLevel,2046) {2047 const flags = fiber.flags;2048 if (flags & ContentReset) {2049 commitResetTextContent(fiber);2050 }2051 if (flags & Ref) {2052 const current = fiber.alternate;2053 if (current !== null) {2054 commitDetachRef(current);2055 }2056 if (enableScopeAPI) {2057 // TODO: This is a temporary solution that allowed us to transition away from React Flare on www.2058 if (fiber.tag === ScopeComponent) {2059 commitAttachRef(fiber);2060 }2061 }2062 }2063 // The following switch statement is only concerned about placement,2064 // updates, and deletions. To avoid needing to add a case for every possible2065 // bitmap value, we remove the secondary effects from the effect tag and2066 // switch on that value.2067 const primaryFlags = flags & (Placement | Update | Hydrating);2068 switch (primaryFlags) {2069 case Placement: {2070 commitPlacement(fiber);2071 // Clear the "placement" from effect tag so that we know that this is2072 // inserted, before any life-cycles like componentDidMount gets called.2073 // TODO: findDOMNode doesn't rely on this any more but isMounted does2074 // and isMounted is deprecated anyway so we should be able to kill this.2075 fiber.flags &= ~Placement;2076 break;2077 }2078 case PlacementAndUpdate: {2079 // Placement2080 commitPlacement(fiber);2081 // Clear the "placement" from effect tag so that we know that this is2082 // inserted, before any life-cycles like componentDidMount gets called.2083 fiber.flags &= ~Placement;2084 // Update2085 const current = fiber.alternate;2086 commitWork(current, fiber);2087 break;2088 }2089 case Hydrating: {2090 fiber.flags &= ~Hydrating;2091 break;2092 }2093 case HydratingAndUpdate: {2094 fiber.flags &= ~Hydrating;2095 // Update2096 const current = fiber.alternate;2097 commitWork(current, fiber);2098 break;2099 }2100 case Update: {2101 const current = fiber.alternate;2102 commitWork(current, fiber);2103 break;2104 }2105 }2106}2107function commitMutationEffectsDeletions(2108 deletions: Array<Fiber>,2109 nearestMountedAncestor: Fiber,2110 root: FiberRoot,2111 renderPriorityLevel,2112) {2113 for (let i = 0; i < deletions.length; i++) {2114 const childToDelete = deletions[i];2115 if (__DEV__) {2116 invokeGuardedCallback(2117 null,2118 commitDeletion,2119 null,2120 root,2121 childToDelete,2122 nearestMountedAncestor,2123 renderPriorityLevel,2124 );2125 if (hasCaughtError()) {2126 const error = clearCaughtError();2127 captureCommitPhaseError(childToDelete, nearestMountedAncestor, error);2128 }2129 } else {2130 try {2131 commitDeletion(2132 root,2133 childToDelete,2134 nearestMountedAncestor,2135 renderPriorityLevel,2136 );2137 } catch (error) {2138 captureCommitPhaseError(childToDelete, nearestMountedAncestor, error);2139 }2140 }2141 }2142}2143export function schedulePassiveEffectCallback() {2144 if (!rootDoesHavePassiveEffects) {2145 rootDoesHavePassiveEffects = true;2146 scheduleCallback(NormalSchedulerPriority, () => {2147 flushPassiveEffects();2148 return null;2149 });2150 }2151}2152export function flushPassiveEffects(): boolean {2153 // Returns whether passive effects were flushed.2154 if (pendingPassiveEffectsRenderPriority !== NoSchedulerPriority) {2155 const priorityLevel =2156 pendingPassiveEffectsRenderPriority > NormalSchedulerPriority2157 ? NormalSchedulerPriority2158 : pendingPassiveEffectsRenderPriority;2159 pendingPassiveEffectsRenderPriority = NoSchedulerPriority;2160 if (decoupleUpdatePriorityFromScheduler) {2161 const previousLanePriority = getCurrentUpdateLanePriority();2162 try {2163 setCurrentUpdateLanePriority(2164 schedulerPriorityToLanePriority(priorityLevel),2165 );2166 return runWithPriority(priorityLevel, flushPassiveEffectsImpl);2167 } finally {2168 setCurrentUpdateLanePriority(previousLanePriority);2169 }2170 } else {2171 return runWithPriority(priorityLevel, flushPassiveEffectsImpl);2172 }2173 }2174 return false;2175}2176function flushPassiveMountEffects(root, firstChild: Fiber): void {2177 let fiber = firstChild;2178 while (fiber !== null) {2179 let prevProfilerOnStack = null;2180 if (enableProfilerTimer && enableProfilerCommitHooks) {2181 if (fiber.tag === Profiler) {2182 prevProfilerOnStack = nearestProfilerOnStack;2183 nearestProfilerOnStack = fiber;2184 }2185 }2186 const primarySubtreeFlags = fiber.subtreeFlags & PassiveMask;2187 if (fiber.child !== null && primarySubtreeFlags !== NoFlags) {2188 flushPassiveMountEffects(root, fiber.child);2189 }2190 if ((fiber.flags & Passive) !== NoFlags) {2191 if (__DEV__) {2192 setCurrentDebugFiberInDEV(fiber);2193 invokeGuardedCallback(2194 null,2195 commitPassiveMountOnFiber,2196 null,2197 root,2198 fiber,2199 );2200 if (hasCaughtError()) {2201 const error = clearCaughtError();2202 captureCommitPhaseError(fiber, fiber.return, error);2203 }2204 resetCurrentDebugFiberInDEV();2205 } else {2206 try {2207 commitPassiveMountOnFiber(root, fiber);2208 } catch (error) {2209 captureCommitPhaseError(fiber, fiber.return, error);2210 }2211 }2212 }2213 if (enableProfilerTimer && enableProfilerCommitHooks) {2214 if (fiber.tag === Profiler) {2215 // Bubble times to the next nearest ancestor Profiler.2216 // After we process that Profiler, we'll bubble further up.2217 if (prevProfilerOnStack !== null) {2218 prevProfilerOnStack.stateNode.passiveEffectDuration +=2219 fiber.stateNode.passiveEffectDuration;2220 }2221 nearestProfilerOnStack = prevProfilerOnStack;2222 }2223 }2224 fiber = fiber.sibling;2225 }2226}2227function flushPassiveUnmountEffects(firstChild: Fiber): void {2228 let fiber = firstChild;2229 while (fiber !== null) {2230 const deletions = fiber.deletions;2231 if (deletions !== null) {2232 for (let i = 0; i < deletions.length; i++) {2233 const fiberToDelete = deletions[i];2234 flushPassiveUnmountEffectsInsideOfDeletedTree(fiberToDelete, fiber);2235 // Now that passive effects have been processed, it's safe to detach lingering pointers.2236 detachFiberAfterEffects(fiberToDelete);2237 }2238 }2239 const child = fiber.child;2240 if (child !== null) {2241 // If any children have passive effects then traverse the subtree.2242 // Note that this requires checking subtreeFlags of the current Fiber,2243 // rather than the subtreeFlags/effectsTag of the first child,2244 // since that would not cover passive effects in siblings.2245 const passiveFlags = fiber.subtreeFlags & PassiveMask;2246 if (passiveFlags !== NoFlags) {2247 flushPassiveUnmountEffects(child);2248 }2249 }2250 const primaryFlags = fiber.flags & Passive;2251 if (primaryFlags !== NoFlags) {2252 setCurrentDebugFiberInDEV(fiber);2253 commitPassiveUnmountOnFiber(fiber);2254 resetCurrentDebugFiberInDEV();2255 }2256 fiber = fiber.sibling;2257 }2258}2259function flushPassiveUnmountEffectsInsideOfDeletedTree(2260 fiberToDelete: Fiber,2261 nearestMountedAncestor: Fiber,2262): void {2263 if ((fiberToDelete.subtreeFlags & PassiveStatic) !== NoFlags) {2264 // If any children have passive effects then traverse the subtree.2265 // Note that this requires checking subtreeFlags of the current Fiber,2266 // rather than the subtreeFlags/effectsTag of the first child,2267 // since that would not cover passive effects in siblings.2268 let child = fiberToDelete.child;2269 while (child !== null) {2270 flushPassiveUnmountEffectsInsideOfDeletedTree(2271 child,2272 nearestMountedAncestor,2273 );2274 child = child.sibling;2275 }2276 }2277 if ((fiberToDelete.flags & PassiveStatic) !== NoFlags) {2278 setCurrentDebugFiberInDEV(fiberToDelete);2279 commitPassiveUnmountInsideDeletedTreeOnFiber(2280 fiberToDelete,2281 nearestMountedAncestor,2282 );2283 resetCurrentDebugFiberInDEV();2284 }2285}2286function flushPassiveEffectsImpl() {2287 if (rootWithPendingPassiveEffects === null) {2288 return false;2289 }2290 const root = rootWithPendingPassiveEffects;2291 const lanes = pendingPassiveEffectsLanes;2292 rootWithPendingPassiveEffects = null;2293 pendingPassiveEffectsLanes = NoLanes;2294 invariant(2295 (executionContext & (RenderContext | CommitContext)) === NoContext,2296 'Cannot flush passive effects while already rendering.',2297 );2298 if (__DEV__) {2299 if (enableDebugTracing) {2300 logPassiveEffectsStarted(lanes);2301 }2302 }2303 if (enableSchedulingProfiler) {2304 markPassiveEffectsStarted(lanes);2305 }2306 if (__DEV__) {2307 isFlushingPassiveEffects = true;2308 }2309 const prevExecutionContext = executionContext;2310 executionContext |= CommitContext;2311 const prevInteractions = pushInteractions(root);2312 // It's important that ALL pending passive effect destroy functions are called2313 // before ANY passive effect create functions are called.2314 // Otherwise effects in sibling components might interfere with each other.2315 // e.g. a destroy function in one component may unintentionally override a ref2316 // value set by a create function in another component.2317 // Layout effects have the same constraint.2318 flushPassiveUnmountEffects(root.current);2319 flushPassiveMountEffects(root, root.current);2320 if (__DEV__) {2321 if (enableDebugTracing) {2322 logPassiveEffectsStopped();2323 }2324 }2325 if (enableSchedulingProfiler) {2326 markPassiveEffectsStopped();2327 }2328 if (__DEV__ && enableDoubleInvokingEffects) {2329 commitDoubleInvokeEffectsInDEV(root.current, true);2330 }2331 if (__DEV__) {2332 isFlushingPassiveEffects = false;2333 }2334 if (enableSchedulerTracing) {2335 popInteractions(((prevInteractions: any): Set<Interaction>));2336 finishPendingInteractions(root, lanes);2337 }2338 executionContext = prevExecutionContext;2339 flushSyncCallbackQueue();2340 // If additional passive effects were scheduled, increment a counter. If this2341 // exceeds the limit, we'll fire a warning.2342 nestedPassiveUpdateCount =2343 rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;2344 return true;2345}2346export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {2347 return (2348 legacyErrorBoundariesThatAlreadyFailed !== null &&2349 legacyErrorBoundariesThatAlreadyFailed.has(instance)2350 );2351}2352export function markLegacyErrorBoundaryAsFailed(instance: mixed) {2353 if (legacyErrorBoundariesThatAlreadyFailed === null) {2354 legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);2355 } else {2356 legacyErrorBoundariesThatAlreadyFailed.add(instance);2357 }2358}2359function prepareToThrowUncaughtError(error: mixed) {2360 if (!hasUncaughtError) {2361 hasUncaughtError = true;2362 firstUncaughtError = error;2363 }2364}2365export const onUncaughtError = prepareToThrowUncaughtError;2366function captureCommitPhaseErrorOnRoot(2367 rootFiber: Fiber,2368 sourceFiber: Fiber,2369 error: mixed,2370) {2371 const errorInfo = createCapturedValue(error, sourceFiber);2372 const update = createRootErrorUpdate(rootFiber, errorInfo, (SyncLane: Lane));2373 enqueueUpdate(rootFiber, update);2374 const eventTime = requestEventTime();2375 const root = markUpdateLaneFromFiberToRoot(rootFiber, (SyncLane: Lane));2376 if (root !== null) {2377 markRootUpdated(root, SyncLane, eventTime);2378 ensureRootIsScheduled(root, eventTime);2379 schedulePendingInteractions(root, SyncLane);2380 }2381}2382export function captureCommitPhaseError(2383 sourceFiber: Fiber,2384 nearestMountedAncestor: Fiber | null,2385 error: mixed,2386) {2387 if (sourceFiber.tag === HostRoot) {2388 // Error was thrown at the root. There is no parent, so the root2389 // itself should capture it.2390 captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);2391 return;2392 }2393 let fiber = null;2394 if (skipUnmountedBoundaries) {2395 fiber = nearestMountedAncestor;2396 } else {2397 fiber = sourceFiber.return;2398 }2399 while (fiber !== null) {2400 if (fiber.tag === HostRoot) {2401 captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);2402 return;2403 } else if (fiber.tag === ClassComponent) {2404 const ctor = fiber.type;2405 const instance = fiber.stateNode;2406 if (2407 typeof ctor.getDerivedStateFromError === 'function' ||2408 (typeof instance.componentDidCatch === 'function' &&2409 !isAlreadyFailedLegacyErrorBoundary(instance))2410 ) {2411 const errorInfo = createCapturedValue(error, sourceFiber);2412 const update = createClassErrorUpdate(2413 fiber,2414 errorInfo,2415 (SyncLane: Lane),2416 );2417 enqueueUpdate(fiber, update);2418 const eventTime = requestEventTime();2419 const root = markUpdateLaneFromFiberToRoot(fiber, (SyncLane: Lane));2420 if (root !== null) {2421 markRootUpdated(root, SyncLane, eventTime);2422 ensureRootIsScheduled(root, eventTime);2423 schedulePendingInteractions(root, SyncLane);2424 }2425 return;2426 }2427 }2428 fiber = fiber.return;2429 }2430}2431export function pingSuspendedRoot(2432 root: FiberRoot,2433 wakeable: Wakeable,2434 pingedLanes: Lanes,2435) {2436 const pingCache = root.pingCache;2437 if (pingCache !== null) {2438 // The wakeable resolved, so we no longer need to memoize, because it will2439 // never be thrown again.2440 pingCache.delete(wakeable);2441 }2442 const eventTime = requestEventTime();2443 markRootPinged(root, pingedLanes, eventTime);2444 if (2445 workInProgressRoot === root &&2446 isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)2447 ) {2448 // Received a ping at the same priority level at which we're currently2449 // rendering. We might want to restart this render. This should mirror2450 // the logic of whether or not a root suspends once it completes.2451 // TODO: If we're rendering sync either due to Sync, Batched or expired,2452 // we should probably never restart.2453 // If we're suspended with delay, or if it's a retry, we'll always suspend2454 // so we can always restart.2455 if (2456 workInProgressRootExitStatus === RootSuspendedWithDelay ||2457 (workInProgressRootExitStatus === RootSuspended &&2458 includesOnlyRetries(workInProgressRootRenderLanes) &&2459 now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS)2460 ) {2461 // Restart from the root.2462 prepareFreshStack(root, NoLanes);2463 } else {2464 // Even though we can't restart right now, we might get an2465 // opportunity later. So we mark this render as having a ping.2466 workInProgressRootPingedLanes = mergeLanes(2467 workInProgressRootPingedLanes,2468 pingedLanes,2469 );2470 }2471 }2472 ensureRootIsScheduled(root, eventTime);2473 schedulePendingInteractions(root, pingedLanes);2474}2475function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {2476 // The boundary fiber (a Suspense component or SuspenseList component)2477 // previously was rendered in its fallback state. One of the promises that2478 // suspended it has resolved, which means at least part of the tree was2479 // likely unblocked. Try rendering again, at a new expiration time.2480 if (retryLane === NoLane) {2481 retryLane = requestRetryLane(boundaryFiber);2482 }2483 // TODO: Special case idle priority?2484 const eventTime = requestEventTime();2485 const root = markUpdateLaneFromFiberToRoot(boundaryFiber, retryLane);2486 if (root !== null) {2487 markRootUpdated(root, retryLane, eventTime);2488 ensureRootIsScheduled(root, eventTime);2489 schedulePendingInteractions(root, retryLane);2490 }2491}2492export function retryDehydratedSuspenseBoundary(boundaryFiber: Fiber) {2493 const suspenseState: null | SuspenseState = boundaryFiber.memoizedState;2494 let retryLane = NoLane;2495 if (suspenseState !== null) {2496 retryLane = suspenseState.retryLane;2497 }2498 retryTimedOutBoundary(boundaryFiber, retryLane);2499}2500export function resolveRetryWakeable(boundaryFiber: Fiber, wakeable: Wakeable) {2501 let retryLane = NoLane; // Default2502 let retryCache: WeakSet<Wakeable> | Set<Wakeable> | null;2503 if (enableSuspenseServerRenderer) {2504 switch (boundaryFiber.tag) {2505 case SuspenseComponent:2506 retryCache = boundaryFiber.stateNode;2507 const suspenseState: null | SuspenseState = boundaryFiber.memoizedState;2508 if (suspenseState !== null) {2509 retryLane = suspenseState.retryLane;2510 }2511 break;2512 case SuspenseListComponent:2513 retryCache = boundaryFiber.stateNode;2514 break;2515 default:2516 invariant(2517 false,2518 'Pinged unknown suspense boundary type. ' +2519 'This is probably a bug in React.',2520 );2521 }2522 } else {2523 retryCache = boundaryFiber.stateNode;2524 }2525 if (retryCache !== null) {2526 // The wakeable resolved, so we no longer need to memoize, because it will2527 // never be thrown again.2528 retryCache.delete(wakeable);2529 }2530 retryTimedOutBoundary(boundaryFiber, retryLane);2531}2532// Computes the next Just Noticeable Difference (JND) boundary.2533// The theory is that a person can't tell the difference between small differences in time.2534// Therefore, if we wait a bit longer than necessary that won't translate to a noticeable2535// difference in the experience. However, waiting for longer might mean that we can avoid2536// showing an intermediate loading state. The longer we have already waited, the harder it2537// is to tell small differences in time. Therefore, the longer we've already waited,2538// the longer we can wait additionally. At some point we have to give up though.2539// We pick a train model where the next boundary commits at a consistent schedule.2540// These particular numbers are vague estimates. We expect to adjust them based on research.2541function jnd(timeElapsed: number) {2542 return timeElapsed < 1202543 ? 1202544 : timeElapsed < 4802545 ? 4802546 : timeElapsed < 10802547 ? 10802548 : timeElapsed < 19202549 ? 19202550 : timeElapsed < 30002551 ? 30002552 : timeElapsed < 43202553 ? 43202554 : ceil(timeElapsed / 1960) * 1960;2555}2556function checkForNestedUpdates() {2557 if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {2558 nestedUpdateCount = 0;2559 rootWithNestedUpdates = null;2560 invariant(2561 false,2562 'Maximum update depth exceeded. This can happen when a component ' +2563 'repeatedly calls setState inside componentWillUpdate or ' +2564 'componentDidUpdate. React limits the number of nested updates to ' +2565 'prevent infinite loops.',2566 );2567 }2568 if (__DEV__) {2569 if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {2570 nestedPassiveUpdateCount = 0;2571 console.error(2572 'Maximum update depth exceeded. This can happen when a component ' +2573 "calls setState inside useEffect, but useEffect either doesn't " +2574 'have a dependency array, or one of the dependencies changes on ' +2575 'every render.',2576 );2577 }2578 }2579}2580function flushRenderPhaseStrictModeWarningsInDEV() {2581 if (__DEV__) {2582 ReactStrictModeWarnings.flushLegacyContextWarning();2583 if (warnAboutDeprecatedLifecycles) {2584 ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();2585 }2586 }2587}2588function commitDoubleInvokeEffectsInDEV(2589 fiber: Fiber,2590 hasPassiveEffects: boolean,2591) {2592 if (__DEV__ && enableDoubleInvokingEffects) {2593 setCurrentDebugFiberInDEV(fiber);2594 invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectUnmountInDEV);2595 if (hasPassiveEffects) {2596 invokeEffectsInDev(2597 fiber,2598 MountPassiveDev,2599 invokePassiveEffectUnmountInDEV,2600 );2601 }2602 invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectMountInDEV);2603 if (hasPassiveEffects) {2604 invokeEffectsInDev(fiber, MountPassiveDev, invokePassiveEffectMountInDEV);2605 }2606 resetCurrentDebugFiberInDEV();2607 }2608}2609function invokeEffectsInDev(2610 firstChild: Fiber,2611 fiberFlags: Flags,2612 invokeEffectFn: (fiber: Fiber) => void,2613): void {2614 if (__DEV__ && enableDoubleInvokingEffects) {2615 let fiber = firstChild;2616 while (fiber !== null) {2617 if (fiber.child !== null) {2618 const primarySubtreeFlag = fiber.subtreeFlags & fiberFlags;2619 if (primarySubtreeFlag !== NoFlags) {2620 invokeEffectsInDev(fiber.child, fiberFlags, invokeEffectFn);2621 }2622 }2623 if ((fiber.flags & fiberFlags) !== NoFlags) {2624 invokeEffectFn(fiber);2625 }2626 fiber = fiber.sibling;2627 }2628 }2629}2630let didWarnStateUpdateForNotYetMountedComponent: Set<string> | null = null;2631function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber) {2632 if (__DEV__) {2633 if ((executionContext & RenderContext) !== NoContext) {2634 // We let the other warning about render phase updates deal with this one.2635 return;2636 }2637 if (!(fiber.mode & (BlockingMode | ConcurrentMode))) {2638 return;2639 }2640 const tag = fiber.tag;2641 if (2642 tag !== IndeterminateComponent &&2643 tag !== HostRoot &&2644 tag !== ClassComponent &&2645 tag !== FunctionComponent &&2646 tag !== ForwardRef &&2647 tag !== MemoComponent &&2648 tag !== SimpleMemoComponent &&2649 tag !== Block2650 ) {2651 // Only warn for user-defined components, not internal ones like Suspense.2652 return;2653 }2654 // We show the whole stack but dedupe on the top component's name because2655 // the problematic code almost always lies inside that component.2656 const componentName = getComponentName(fiber.type) || 'ReactComponent';2657 if (didWarnStateUpdateForNotYetMountedComponent !== null) {2658 if (didWarnStateUpdateForNotYetMountedComponent.has(componentName)) {2659 return;2660 }2661 didWarnStateUpdateForNotYetMountedComponent.add(componentName);2662 } else {2663 didWarnStateUpdateForNotYetMountedComponent = new Set([componentName]);2664 }2665 const previousFiber = ReactCurrentFiberCurrent;2666 try {2667 setCurrentDebugFiberInDEV(fiber);2668 console.error(2669 "Can't perform a React state update on a component that hasn't mounted yet. " +2670 'This indicates that you have a side-effect in your render function that ' +2671 'asynchronously later calls tries to update the component. Move this work to ' +2672 'useEffect instead.',2673 );2674 } finally {2675 if (previousFiber) {2676 setCurrentDebugFiberInDEV(fiber);2677 } else {2678 resetCurrentDebugFiberInDEV();2679 }2680 }2681 }2682}2683let didWarnStateUpdateForUnmountedComponent: Set<string> | null = null;2684function warnAboutUpdateOnUnmountedFiberInDEV(fiber) {2685 if (__DEV__) {2686 const tag = fiber.tag;2687 if (2688 tag !== HostRoot &&2689 tag !== ClassComponent &&2690 tag !== FunctionComponent &&2691 tag !== ForwardRef &&2692 tag !== MemoComponent &&2693 tag !== SimpleMemoComponent &&2694 tag !== Block2695 ) {2696 // Only warn for user-defined components, not internal ones like Suspense.2697 return;2698 }...

Full Screen

Full Screen

ReactFiberWorkLoop.old.js

Source:ReactFiberWorkLoop.old.js Github

copy

Full Screen

...182 checkForNestedUpdates();183 warnAboutRenderPhaseUpdatesInDEV(fiber);184 var root = markUpdateLaneFromFiberToRoot(fiber, lane);185 if (root === null) {186 warnAboutUpdateOnUnmountedFiberInDEV(fiber);187 return null;188 } // Mark that the root has a pending update.189 markRootUpdated(root, lane, eventTime);190 if (root === workInProgressRoot) {191 // Received an update to a tree that's in the middle of rendering. Mark192 // that there was an interleaved update work on this root. Unless the193 // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render194 // phase update. In that case, we don't treat render phase updates as if195 // they were interleaved, for backwards compat reasons.196 {197 workInProgressRootUpdatedLanes = mergeLanes(workInProgressRootUpdatedLanes, lane);198 }199 if (workInProgressRootExitStatus === RootSuspendedWithDelay) {200 // The root already suspended with a delay, which means this render201 // definitely won't finish. Since we have a new update, let's mark it as202 // suspended now, right before marking the incoming update. This has the203 // effect of interrupting the current render and switching to the update.204 // TODO: Make sure this doesn't override pings that happen while we've205 // already started rendering.206 markRootSuspended$1(root, workInProgressRootRenderLanes);207 }208 } // TODO: requestUpdateLanePriority also reads the priority. Pass the209 // priority as an argument to that function and this one.210 var priorityLevel = getCurrentPriorityLevel();211 if (lane === SyncLane) {212 if ( // Check if we're inside unbatchedUpdates213 (executionContext & LegacyUnbatchedContext) !== NoContext && // Check if we're not already rendering214 (executionContext & (RenderContext | CommitContext)) === NoContext) {215 // Register pending interactions on the root to avoid losing traced interaction data.216 schedulePendingInteractions(root, lane); // This is a legacy edge case. The initial mount of a ReactDOM.render-ed217 // root inside of batchedUpdates should be synchronous, but layout updates218 // should be deferred until the end of the batch.219 performSyncWorkOnRoot(root);220 } else {221 ensureRootIsScheduled(root, eventTime);222 schedulePendingInteractions(root, lane);223 if (executionContext === NoContext) {224 // Flush the synchronous work now, unless we're already working or inside225 // a batch. This is intentionally inside scheduleUpdateOnFiber instead of226 // scheduleCallbackForFiber to preserve the ability to schedule a callback227 // without immediately flushing it. We only do this for user-initiated228 // updates, to preserve historical behavior of legacy mode.229 resetRenderTimer();230 flushSyncCallbackQueue();231 }232 }233 } else {234 // Schedule a discrete update but only if it's not Sync.235 if ((executionContext & DiscreteEventContext) !== NoContext && ( // Only updates at user-blocking priority or greater are considered236 // discrete, even inside a discrete event.237 priorityLevel === UserBlockingPriority$2 || priorityLevel === ImmediatePriority$1)) {238 // This is the result of a discrete event. Track the lowest priority239 // discrete update per root so we can flush them early, if needed.240 if (rootsWithPendingDiscreteUpdates === null) {241 rootsWithPendingDiscreteUpdates = new Set([root]);242 } else {243 rootsWithPendingDiscreteUpdates.add(root);244 }245 } // Schedule other updates after in case the callback is sync.246 ensureRootIsScheduled(root, eventTime);247 schedulePendingInteractions(root, lane);248 } // We use this when assigning a lane for a transition inside249 // `requestUpdateLane`. We assume it's the same as the root being updated,250 // since in the common case of a single root app it probably is. If it's not251 // the same root, then it's not a huge deal, we just might batch more stuff252 // together more than necessary.253 mostRecentlyUpdatedRoot = root;254 } // This is split into a separate function so we can mark a fiber with pending255 // work without treating it as a typical update that originates from an event;256 // e.g. retrying a Suspense boundary isn't an update, but it does schedule work257 // on a fiber.258 function markUpdateLaneFromFiberToRoot(sourceFiber, lane) {259 // Update the source fiber's lanes260 sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);261 var alternate = sourceFiber.alternate;262 if (alternate !== null) {263 alternate.lanes = mergeLanes(alternate.lanes, lane);264 }265 {266 if (alternate === null && (sourceFiber.flags & (Placement | Hydrating)) !== NoFlags) {267 warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);268 }269 } // Walk the parent path to the root and update the child expiration time.270 var node = sourceFiber;271 var parent = sourceFiber.return;272 while (parent !== null) {273 parent.childLanes = mergeLanes(parent.childLanes, lane);274 alternate = parent.alternate;275 if (alternate !== null) {276 alternate.childLanes = mergeLanes(alternate.childLanes, lane);277 } else {278 {279 if ((parent.flags & (Placement | Hydrating)) !== NoFlags) {280 warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);281 }282 }283 }284 node = parent;285 parent = parent.return;286 }287 if (node.tag === HostRoot) {288 var root = node.stateNode;289 return root;290 } else {291 return null;292 }293 } // Use this function to schedule a task for a root. There's only one task per294 // root; if a task was already scheduled, we'll check to make sure the priority295 // of the existing task is the same as the priority of the next level that the296 // root has work on. This function is called on every update, and right before297 // exiting a task.298 function ensureRootIsScheduled(root, currentTime) {299 var existingCallbackNode = root.callbackNode; // Check if any lanes are being starved by other work. If so, mark them as300 // expired so we know to work on those next.301 markStarvedLanesAsExpired(root, currentTime); // Determine the next lanes to work on, and their priority.302 var nextLanes = getNextLanes(root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes); // This returns the priority level computed during the `getNextLanes` call.303 var newCallbackPriority = returnNextLanesPriority();304 if (nextLanes === NoLanes) {305 // Special case: There's nothing to work on.306 if (existingCallbackNode !== null) {307 cancelCallback(existingCallbackNode);308 root.callbackNode = null;309 root.callbackPriority = NoLanePriority;310 }311 return;312 } // Check if there's an existing task. We may be able to reuse it.313 if (existingCallbackNode !== null) {314 var existingCallbackPriority = root.callbackPriority;315 if (existingCallbackPriority === newCallbackPriority) {316 // The priority hasn't changed. We can reuse the existing task. Exit.317 return;318 } // The priority changed. Cancel the existing callback. We'll schedule a new319 // one below.320 cancelCallback(existingCallbackNode);321 } // Schedule a new callback.322 var newCallbackNode;323 if (newCallbackPriority === SyncLanePriority) {324 // Special case: Sync React callbacks are scheduled on a special325 // internal queue326 newCallbackNode = scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));327 } else if (newCallbackPriority === SyncBatchedLanePriority) {328 newCallbackNode = scheduleCallback(ImmediatePriority$1, performSyncWorkOnRoot.bind(null, root));329 } else {330 var schedulerPriorityLevel = lanePriorityToSchedulerPriority(newCallbackPriority);331 newCallbackNode = scheduleCallback(schedulerPriorityLevel, performConcurrentWorkOnRoot.bind(null, root));332 }333 root.callbackPriority = newCallbackPriority;334 root.callbackNode = newCallbackNode;335 } // This is the entry point for every concurrent task, i.e. anything that336 // goes through Scheduler.337 function performConcurrentWorkOnRoot(root) {338 // Since we know we're in a React event, we can clear the current339 // event time. The next update will compute a new event time.340 currentEventTime = NoTimestamp;341 currentEventWipLanes = NoLanes;342 currentEventPendingLanes = NoLanes;343 if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) {344 {345 throw Error( "Should not already be working." );346 }347 } // Flush any pending passive effects before deciding which lanes to work on,348 // in case they schedule additional work.349 var originalCallbackNode = root.callbackNode;350 var didFlushPassiveEffects = flushPassiveEffects();351 if (didFlushPassiveEffects) {352 // Something in the passive effect phase may have canceled the current task.353 // Check if the task node for this root was changed.354 if (root.callbackNode !== originalCallbackNode) {355 // The current task was canceled. Exit. We don't need to call356 // `ensureRootIsScheduled` because the check above implies either that357 // there's a new task, or that there's no remaining work on this root.358 return null;359 }360 } // Determine the next expiration time to work on, using the fields stored361 // on the root.362 var lanes = getNextLanes(root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes);363 if (lanes === NoLanes) {364 // Defensive coding. This is never expected to happen.365 return null;366 }367 var exitStatus = renderRootConcurrent(root, lanes);368 if (includesSomeLane(workInProgressRootIncludedLanes, workInProgressRootUpdatedLanes)) {369 // The render included lanes that were updated during the render phase.370 // For example, when unhiding a hidden tree, we include all the lanes371 // that were previously skipped when the tree was hidden. That set of372 // lanes is a superset of the lanes we started rendering with.373 //374 // So we'll throw out the current work and restart.375 prepareFreshStack(root, NoLanes);376 } else if (exitStatus !== RootIncomplete) {377 if (exitStatus === RootErrored) {378 executionContext |= RetryAfterError; // If an error occurred during hydration,379 // discard server response and fall back to client side render.380 if (root.hydrate) {381 root.hydrate = false;382 clearContainer(root.containerInfo);383 } // If something threw an error, try rendering one more time. We'll render384 // synchronously to block concurrent data mutations, and we'll includes385 // all pending updates are included. If it still fails after the second386 // attempt, we'll give up and commit the resulting tree.387 lanes = getLanesToRetrySynchronouslyOnError(root);388 if (lanes !== NoLanes) {389 exitStatus = renderRootSync(root, lanes);390 }391 }392 if (exitStatus === RootFatalErrored) {393 var fatalError = workInProgressRootFatalError;394 prepareFreshStack(root, NoLanes);395 markRootSuspended$1(root, lanes);396 ensureRootIsScheduled(root, now());397 throw fatalError;398 } // We now have a consistent tree. The next step is either to commit it,399 // or, if something suspended, wait to commit it after a timeout.400 var finishedWork = root.current.alternate;401 root.finishedWork = finishedWork;402 root.finishedLanes = lanes;403 finishConcurrentRender(root, exitStatus, lanes);404 }405 ensureRootIsScheduled(root, now());406 if (root.callbackNode === originalCallbackNode) {407 // The task node scheduled for this root is the same one that's408 // currently executed. Need to return a continuation.409 return performConcurrentWorkOnRoot.bind(null, root);410 }411 return null;412 }413 function finishConcurrentRender(root, exitStatus, lanes) {414 switch (exitStatus) {415 case RootIncomplete:416 case RootFatalErrored:417 {418 {419 {420 throw Error( "Root did not complete. This is a bug in React." );421 }422 }423 }424 // Flow knows about invariant, so it complains if I add a break425 // statement, but eslint doesn't know about invariant, so it complains426 // if I do. eslint-disable-next-line no-fallthrough427 case RootErrored:428 {429 // We should have already attempted to retry this tree. If we reached430 // this point, it errored again. Commit it.431 commitRoot(root);432 break;433 }434 case RootSuspended:435 {436 markRootSuspended$1(root, lanes); // We have an acceptable loading state. We need to figure out if we437 // should immediately commit it or wait a bit.438 if (includesOnlyRetries(lanes) && // do not delay if we're inside an act() scope439 !shouldForceFlushFallbacksInDEV()) {440 // This render only included retries, no updates. Throttle committing441 // retries so that we don't show too many loading states too quickly.442 var msUntilTimeout = globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now(); // Don't bother with a very short suspense time.443 if (msUntilTimeout > 10) {444 var nextLanes = getNextLanes(root, NoLanes);445 if (nextLanes !== NoLanes) {446 // There's additional work on this root.447 break;448 }449 var suspendedLanes = root.suspendedLanes;450 if (!isSubsetOfLanes(suspendedLanes, lanes)) {451 // We should prefer to render the fallback of at the last452 // suspended level. Ping the last suspended level to try453 // rendering it again.454 // FIXME: What if the suspended lanes are Idle? Should not restart.455 var eventTime = requestEventTime();456 markRootPinged(root, suspendedLanes);457 break;458 } // The render is suspended, it hasn't timed out, and there's no459 // lower priority work to do. Instead of committing the fallback460 // immediately, wait for more data to arrive.461 root.timeoutHandle = scheduleTimeout(commitRoot.bind(null, root), msUntilTimeout);462 break;463 }464 } // The work expired. Commit immediately.465 commitRoot(root);466 break;467 }468 case RootSuspendedWithDelay:469 {470 markRootSuspended$1(root, lanes);471 if (includesOnlyTransitions(lanes)) {472 // This is a transition, so we should exit without committing a473 // placeholder and without scheduling a timeout. Delay indefinitely474 // until we receive more data.475 break;476 }477 if (!shouldForceFlushFallbacksInDEV()) {478 // This is not a transition, but we did trigger an avoided state.479 // Schedule a placeholder to display after a short delay, using the Just480 // Noticeable Difference.481 // TODO: Is the JND optimization worth the added complexity? If this is482 // the only reason we track the event time, then probably not.483 // Consider removing.484 var mostRecentEventTime = getMostRecentEventTime(root, lanes);485 var eventTimeMs = mostRecentEventTime;486 var timeElapsedMs = now() - eventTimeMs;487 var _msUntilTimeout = jnd(timeElapsedMs) - timeElapsedMs; // Don't bother with a very short suspense time.488 if (_msUntilTimeout > 10) {489 // Instead of committing the fallback immediately, wait for more data490 // to arrive.491 root.timeoutHandle = scheduleTimeout(commitRoot.bind(null, root), _msUntilTimeout);492 break;493 }494 } // Commit the placeholder.495 commitRoot(root);496 break;497 }498 case RootCompleted:499 {500 // The work completed. Ready to commit.501 commitRoot(root);502 break;503 }504 default:505 {506 {507 {508 throw Error( "Unknown root exit status." );509 }510 }511 }512 }513 }514 function markRootSuspended$1(root, suspendedLanes) {515 // When suspending, we should always exclude lanes that were pinged or (more516 // rarely, since we try to avoid it) updated during the render phase.517 // TODO: Lol maybe there's a better way to factor this besides this518 // obnoxiously named function :)519 suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);520 suspendedLanes = removeLanes(suspendedLanes, workInProgressRootUpdatedLanes);521 markRootSuspended(root, suspendedLanes);522 } // This is the entry point for synchronous tasks that don't go523 // through Scheduler524 function performSyncWorkOnRoot(root) {525 if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) {526 {527 throw Error( "Should not already be working." );528 }529 }530 flushPassiveEffects();531 var lanes;532 var exitStatus;533 if (root === workInProgressRoot && includesSomeLane(root.expiredLanes, workInProgressRootRenderLanes)) {534 // There's a partial tree, and at least one of its lanes has expired. Finish535 // rendering it before rendering the rest of the expired work.536 lanes = workInProgressRootRenderLanes;537 exitStatus = renderRootSync(root, lanes);538 if (includesSomeLane(workInProgressRootIncludedLanes, workInProgressRootUpdatedLanes)) {539 // The render included lanes that were updated during the render phase.540 // For example, when unhiding a hidden tree, we include all the lanes541 // that were previously skipped when the tree was hidden. That set of542 // lanes is a superset of the lanes we started rendering with.543 //544 // Note that this only happens when part of the tree is rendered545 // concurrently. If the whole tree is rendered synchronously, then there546 // are no interleaved events.547 lanes = getNextLanes(root, lanes);548 exitStatus = renderRootSync(root, lanes);549 }550 } else {551 lanes = getNextLanes(root, NoLanes);552 exitStatus = renderRootSync(root, lanes);553 }554 if (root.tag !== LegacyRoot && exitStatus === RootErrored) {555 executionContext |= RetryAfterError; // If an error occurred during hydration,556 // discard server response and fall back to client side render.557 if (root.hydrate) {558 root.hydrate = false;559 clearContainer(root.containerInfo);560 } // If something threw an error, try rendering one more time. We'll render561 // synchronously to block concurrent data mutations, and we'll includes562 // all pending updates are included. If it still fails after the second563 // attempt, we'll give up and commit the resulting tree.564 lanes = getLanesToRetrySynchronouslyOnError(root);565 if (lanes !== NoLanes) {566 exitStatus = renderRootSync(root, lanes);567 }568 }569 if (exitStatus === RootFatalErrored) {570 var fatalError = workInProgressRootFatalError;571 prepareFreshStack(root, NoLanes);572 markRootSuspended$1(root, lanes);573 ensureRootIsScheduled(root, now());574 throw fatalError;575 } // We now have a consistent tree. Because this is a sync render, we576 // will commit it even if something suspended.577 var finishedWork = root.current.alternate;578 root.finishedWork = finishedWork;579 root.finishedLanes = lanes;580 commitRoot(root); // Before exiting, make sure there's a callback scheduled for the next581 // pending level.582 ensureRootIsScheduled(root, now());583 return null;584 }585 function flushRoot(root, lanes) {586 markRootExpired(root, lanes);587 ensureRootIsScheduled(root, now());588 if ((executionContext & (RenderContext | CommitContext)) === NoContext) {589 resetRenderTimer();590 flushSyncCallbackQueue();591 }592 }593 function getExecutionContext() {594 return executionContext;595 }596 // flush react事件597 function flushDiscreteUpdates() {598 // TODO: Should be able to flush inside batchedUpdates, but not inside `act`.599 // However, `act` uses `batchedUpdates`, so there's no way to distinguish600 // those two cases. Need to fix this before exposing flushDiscreteUpdates601 // as a public API.602 if ((executionContext & (BatchedContext | RenderContext | CommitContext)) !== NoContext) {603 {604 if ((executionContext & RenderContext) !== NoContext) {605 error('unstable_flushDiscreteUpdates: Cannot flush updates when React is ' + 'already rendering.');606 }607 } // We're already rendering, so we can't synchronously flush pending work.608 // This is probably a nested event dispatch triggered by a lifecycle/effect,609 // like `el.focus()`. Exit.610 return;611 }612 flushPendingDiscreteUpdates(); // If the discrete updates scheduled passive effects, flush them now so that613 // they fire before the next serial event.614 flushPassiveEffects();615 }616 function flushPendingDiscreteUpdates() {617 if (rootsWithPendingDiscreteUpdates !== null) {618 // For each root with pending discrete updates, schedule a callback to619 // immediately flush them.620 var roots = rootsWithPendingDiscreteUpdates;621 rootsWithPendingDiscreteUpdates = null;622 roots.forEach(function (root) {623 markDiscreteUpdatesExpired(root);624 ensureRootIsScheduled(root, now());625 });626 } // Now flush the immediate queue.627 flushSyncCallbackQueue();628 }629 function batchedUpdates$1(fn, a) {630 var prevExecutionContext = executionContext;631 executionContext |= BatchedContext;632 try {633 return fn(a);634 } finally {635 executionContext = prevExecutionContext;636 if (executionContext === NoContext) {637 // Flush the immediate callbacks that were scheduled during this batch638 resetRenderTimer();639 flushSyncCallbackQueue();640 }641 }642 }643 function batchedEventUpdates$1(fn, a) {644 var prevExecutionContext = executionContext;645 executionContext |= EventContext;646 try {647 return fn(a);648 } finally {649 executionContext = prevExecutionContext;650 if (executionContext === NoContext) {651 // Flush the immediate callbacks that were scheduled during this batch652 resetRenderTimer();653 flushSyncCallbackQueue();654 }655 }656 }657 function discreteUpdates$1(fn, a, b, c, d) {658 var prevExecutionContext = executionContext;659 executionContext |= DiscreteEventContext;660 {661 try {662 return runWithPriority$1(UserBlockingPriority$2, fn.bind(null, a, b, c, d));663 } finally {664 executionContext = prevExecutionContext;665 if (executionContext === NoContext) {666 // Flush the immediate callbacks that were scheduled during this batch667 resetRenderTimer();668 flushSyncCallbackQueue();669 }670 }671 }672 }673 // 非批量更新674 function unbatchedUpdates(fn, a) {675 var prevExecutionContext = executionContext;676 executionContext &= ~BatchedContext;677 executionContext |= LegacyUnbatchedContext;678 try {679 return fn(a);680 } finally {681 executionContext = prevExecutionContext;682 if (executionContext === NoContext) {683 // Flush the immediate callbacks that were scheduled during this batch684 resetRenderTimer();685 flushSyncCallbackQueue();686 }687 }688 }689 function flushSync(fn, a) {690 var prevExecutionContext = executionContext;691 if ((prevExecutionContext & (RenderContext | CommitContext)) !== NoContext) {692 {693 error('flushSync was called from inside a lifecycle method. React cannot ' + 'flush when React is already rendering. Consider moving this call to ' + 'a scheduler task or micro task.');694 }695 return fn(a);696 }697 executionContext |= BatchedContext;698 {699 try {700 if (fn) {701 return runWithPriority$1(ImmediatePriority$1, fn.bind(null, a));702 } else {703 return undefined;704 }705 } finally {706 executionContext = prevExecutionContext; // Flush the immediate callbacks that were scheduled during this batch.707 // Note that this will happen even if batchedUpdates is higher up708 // the stack.709 flushSyncCallbackQueue();710 }711 }712 }713 function flushControlled(fn) {714 var prevExecutionContext = executionContext;715 executionContext |= BatchedContext;716 {717 try {718 runWithPriority$1(ImmediatePriority$1, fn);719 } finally {720 executionContext = prevExecutionContext;721 if (executionContext === NoContext) {722 // Flush the immediate callbacks that were scheduled during this batch723 resetRenderTimer();724 flushSyncCallbackQueue();725 }726 }727 }728 }729 function pushRenderLanes(fiber, lanes) {730 push(subtreeRenderLanesCursor, subtreeRenderLanes, fiber);731 subtreeRenderLanes = mergeLanes(subtreeRenderLanes, lanes);732 workInProgressRootIncludedLanes = mergeLanes(workInProgressRootIncludedLanes, lanes);733 }734 function popRenderLanes(fiber) {735 subtreeRenderLanes = subtreeRenderLanesCursor.current;736 pop(subtreeRenderLanesCursor, fiber);737 }738 function prepareFreshStack(root, lanes) {739 root.finishedWork = null;740 root.finishedLanes = NoLanes;741 var timeoutHandle = root.timeoutHandle;742 if (timeoutHandle !== noTimeout) {743 // The root previous suspended and scheduled a timeout to commit a fallback744 // state. Now that we have additional work, cancel the timeout.745 root.timeoutHandle = noTimeout; // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above746 cancelTimeout(timeoutHandle);747 }748 if (workInProgress !== null) {749 var interruptedWork = workInProgress.return;750 while (interruptedWork !== null) {751 unwindInterruptedWork(interruptedWork);752 interruptedWork = interruptedWork.return;753 }754 }755 workInProgressRoot = root;756 workInProgress = createWorkInProgress(root.current, null);757 workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;758 workInProgressRootExitStatus = RootIncomplete;759 workInProgressRootFatalError = null;760 workInProgressRootSkippedLanes = NoLanes;761 workInProgressRootUpdatedLanes = NoLanes;762 workInProgressRootPingedLanes = NoLanes;763 {764 spawnedWorkDuringRender = null;765 }766 {767 ReactStrictModeWarnings.discardPendingWarnings();768 }769 }770 function handleError(root, thrownValue) {771 do {772 var erroredWork = workInProgress;773 try {774 // Reset module-level state that was set during the render phase.775 resetContextDependencies();776 resetHooksAfterThrow();777 resetCurrentFiber(); // TODO: I found and added this missing line while investigating a778 // separate issue. Write a regression test using string refs.779 ReactCurrentOwner$2.current = null;780 if (erroredWork === null || erroredWork.return === null) {781 // Expected to be working on a non-root fiber. This is a fatal error782 // because there's no ancestor that can handle it; the root is783 // supposed to capture all errors that weren't caught by an error784 // boundary.785 workInProgressRootExitStatus = RootFatalErrored;786 workInProgressRootFatalError = thrownValue; // Set `workInProgress` to null. This represents advancing to the next787 // sibling, or the parent if there are no siblings. But since the root788 // has no siblings nor a parent, we set it to null. Usually this is789 // handled by `completeUnitOfWork` or `unwindWork`, but since we're790 // intentionally not calling those, we need set it here.791 // TODO: Consider calling `unwindWork` to pop the contexts.792 workInProgress = null;793 return;794 }795 if (enableProfilerTimer && erroredWork.mode & ProfileMode) {796 // Record the time spent rendering before an error was thrown. This797 // avoids inaccurate Profiler durations in the case of a798 // suspended render.799 stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true);800 }801 throwException(root, erroredWork.return, erroredWork, thrownValue, workInProgressRootRenderLanes);802 completeUnitOfWork(erroredWork);803 } catch (yetAnotherThrownValue) {804 // Something in the return path also threw.805 thrownValue = yetAnotherThrownValue;806 if (workInProgress === erroredWork && erroredWork !== null) {807 // If this boundary has already errored, then we had trouble processing808 // the error. Bubble it to the next boundary.809 erroredWork = erroredWork.return;810 workInProgress = erroredWork;811 } else {812 erroredWork = workInProgress;813 }814 continue;815 } // Return to the normal work loop.816 return;817 } while (true);818 }819 function pushDispatcher() {820 var prevDispatcher = ReactCurrentDispatcher$2.current;821 ReactCurrentDispatcher$2.current = ContextOnlyDispatcher;822 if (prevDispatcher === null) {823 // The React isomorphic package does not include a default dispatcher.824 // Instead the first renderer will lazily attach one, in order to give825 // nicer error messages.826 return ContextOnlyDispatcher;827 } else {828 return prevDispatcher;829 }830 }831 function popDispatcher(prevDispatcher) {832 ReactCurrentDispatcher$2.current = prevDispatcher;833 }834 function pushInteractions(root) {835 {836 var prevInteractions = __interactionsRef.current;837 __interactionsRef.current = root.memoizedInteractions;838 return prevInteractions;839 }840 }841 function popInteractions(prevInteractions) {842 {843 __interactionsRef.current = prevInteractions;844 }845 }846 function markCommitTimeOfFallback() {847 globalMostRecentFallbackTime = now();848 }849 function markSkippedUpdateLanes(lane) {850 workInProgressRootSkippedLanes = mergeLanes(lane, workInProgressRootSkippedLanes);851 }852 function renderDidSuspend() {853 if (workInProgressRootExitStatus === RootIncomplete) {854 workInProgressRootExitStatus = RootSuspended;855 }856 }857 function renderDidSuspendDelayIfPossible() {858 if (workInProgressRootExitStatus === RootIncomplete || workInProgressRootExitStatus === RootSuspended) {859 workInProgressRootExitStatus = RootSuspendedWithDelay;860 } // Check if there are updates that we skipped tree that might have unblocked861 // this render.862 if (workInProgressRoot !== null && (includesNonIdleWork(workInProgressRootSkippedLanes) || includesNonIdleWork(workInProgressRootUpdatedLanes))) {863 // Mark the current render as suspended so that we switch to working on864 // the updates that were skipped. Usually we only suspend at the end of865 // the render phase.866 // TODO: We should probably always mark the root as suspended immediately867 // (inside this function), since by suspending at the end of the render868 // phase introduces a potential mistake where we suspend lanes that were869 // pinged or updated while we were rendering.870 markRootSuspended$1(workInProgressRoot, workInProgressRootRenderLanes);871 }872 }873 function renderDidError() {874 if (workInProgressRootExitStatus !== RootCompleted) {875 workInProgressRootExitStatus = RootErrored;876 }877 } // Called during render to determine if anything has suspended.878 // Returns false if we're not sure.879 function renderHasNotSuspendedYet() {880 // If something errored or completed, we can't really be sure,881 // so those are false.882 return workInProgressRootExitStatus === RootIncomplete;883 }884 function renderRootSync(root, lanes) {885 var prevExecutionContext = executionContext;886 executionContext |= RenderContext;887 var prevDispatcher = pushDispatcher(); // If the root or lanes have changed, throw out the existing stack888 // and prepare a fresh one. Otherwise we'll continue where we left off.889 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {890 prepareFreshStack(root, lanes);891 startWorkOnPendingInteractions(root, lanes);892 }893 var prevInteractions = pushInteractions(root);894 {895 markRenderStarted(lanes);896 }897 do {898 try {899 workLoopSync();900 break;901 } catch (thrownValue) {902 handleError(root, thrownValue);903 }904 } while (true);905 resetContextDependencies();906 {907 popInteractions(prevInteractions);908 }909 executionContext = prevExecutionContext;910 popDispatcher(prevDispatcher);911 if (workInProgress !== null) {912 // This is a sync render, so we should have finished the whole tree.913 {914 {915 throw Error( "Cannot commit an incomplete root. This error is likely caused by a bug in React. Please file an issue." );916 }917 }918 }919 {920 markRenderStopped();921 } // Set this to null to indicate there's no in-progress render.922 workInProgressRoot = null;923 workInProgressRootRenderLanes = NoLanes;924 return workInProgressRootExitStatus;925 } // The work loop is an extremely hot path. Tell Closure not to inline it.926 /** @noinline */927 function workLoopSync() {928 // Already timed out, so perform work without checking if we need to yield.929 while (workInProgress !== null) {930 performUnitOfWork(workInProgress);931 }932 }933 function renderRootConcurrent(root, lanes) {934 var prevExecutionContext = executionContext;935 executionContext |= RenderContext;936 var prevDispatcher = pushDispatcher(); // If the root or lanes have changed, throw out the existing stack937 // and prepare a fresh one. Otherwise we'll continue where we left off.938 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {939 resetRenderTimer();940 prepareFreshStack(root, lanes);941 startWorkOnPendingInteractions(root, lanes);942 }943 var prevInteractions = pushInteractions(root);944 {945 markRenderStarted(lanes);946 }947 do {948 try {949 workLoopConcurrent();950 break;951 } catch (thrownValue) {952 handleError(root, thrownValue);953 }954 } while (true);955 resetContextDependencies();956 {957 popInteractions(prevInteractions);958 }959 popDispatcher(prevDispatcher);960 executionContext = prevExecutionContext;961 if (workInProgress !== null) {962 // Still work remaining.963 {964 markRenderYielded();965 }966 return RootIncomplete;967 } else {968 // Completed the tree.969 {970 markRenderStopped();971 } // Set this to null to indicate there's no in-progress render.972 workInProgressRoot = null;973 workInProgressRootRenderLanes = NoLanes; // Return the final exit status.974 return workInProgressRootExitStatus;975 }976 }977 /** @noinline */978 function workLoopConcurrent() {979 // Perform work until Scheduler asks us to yield980 while (workInProgress !== null && !shouldYield()) {981 performUnitOfWork(workInProgress);982 }983 }984 function performUnitOfWork(unitOfWork) {985 // The current, flushed, state of this fiber is the alternate. Ideally986 // nothing should rely on this, but relying on it here means that we don't987 // need an additional field on the work in progress.988 var current = unitOfWork.alternate;989 setCurrentFiber(unitOfWork);990 var next;991 if ( (unitOfWork.mode & ProfileMode) !== NoMode) {992 startProfilerTimer(unitOfWork);993 next = beginWork$1(current, unitOfWork, subtreeRenderLanes);994 stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);995 } else {996 next = beginWork$1(current, unitOfWork, subtreeRenderLanes);997 }998 resetCurrentFiber();999 unitOfWork.memoizedProps = unitOfWork.pendingProps;1000 if (next === null) {1001 // If this doesn't spawn new work, complete the current work.1002 completeUnitOfWork(unitOfWork);1003 } else {1004 workInProgress = next;1005 }1006 ReactCurrentOwner$2.current = null;1007 }1008 function completeUnitOfWork(unitOfWork) {1009 // Attempt to complete the current unit of work, then move to the next1010 // sibling. If there are no more siblings, return to the parent fiber.1011 var completedWork = unitOfWork;1012 do {1013 // The current, flushed, state of this fiber is the alternate. Ideally1014 // nothing should rely on this, but relying on it here means that we don't1015 // need an additional field on the work in progress.1016 var current = completedWork.alternate;1017 var returnFiber = completedWork.return; // Check if the work completed or if something threw.1018 if ((completedWork.flags & Incomplete) === NoFlags) {1019 setCurrentFiber(completedWork);1020 var next = void 0;1021 if ( (completedWork.mode & ProfileMode) === NoMode) {1022 next = completeWork(current, completedWork, subtreeRenderLanes);1023 } else {1024 startProfilerTimer(completedWork);1025 next = completeWork(current, completedWork, subtreeRenderLanes); // Update render duration assuming we didn't error.1026 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1027 }1028 resetCurrentFiber();1029 if (next !== null) {1030 // Completing this fiber spawned new work. Work on that next.1031 workInProgress = next;1032 return;1033 }1034 resetChildLanes(completedWork);1035 if (returnFiber !== null && // Do not append effects to parents if a sibling failed to complete1036 (returnFiber.flags & Incomplete) === NoFlags) {1037 // Append all the effects of the subtree and this fiber onto the effect1038 // list of the parent. The completion order of the children affects the1039 // side-effect order.1040 if (returnFiber.firstEffect === null) {1041 returnFiber.firstEffect = completedWork.firstEffect;1042 }1043 if (completedWork.lastEffect !== null) {1044 if (returnFiber.lastEffect !== null) {1045 returnFiber.lastEffect.nextEffect = completedWork.firstEffect;1046 }1047 returnFiber.lastEffect = completedWork.lastEffect;1048 } // If this fiber had side-effects, we append it AFTER the children's1049 // side-effects. We can perform certain side-effects earlier if needed,1050 // by doing multiple passes over the effect list. We don't want to1051 // schedule our own side-effect on our own list because if end up1052 // reusing children we'll schedule this effect onto itself since we're1053 // at the end.1054 var flags = completedWork.flags; // Skip both NoWork and PerformedWork tags when creating the effect1055 // list. PerformedWork effect is read by React DevTools but shouldn't be1056 // committed.1057 if (flags > PerformedWork) {1058 if (returnFiber.lastEffect !== null) {1059 returnFiber.lastEffect.nextEffect = completedWork;1060 } else {1061 returnFiber.firstEffect = completedWork;1062 }1063 returnFiber.lastEffect = completedWork;1064 }1065 }1066 } else {1067 // This fiber did not complete because something threw. Pop values off1068 // the stack without entering the complete phase. If this is a boundary,1069 // capture values if possible.1070 var _next = unwindWork(completedWork); // Because this fiber did not complete, don't reset its expiration time.1071 if (_next !== null) {1072 // If completing this work spawned new work, do that next. We'll come1073 // back here again.1074 // Since we're restarting, remove anything that is not a host effect1075 // from the effect tag.1076 _next.flags &= HostEffectMask;1077 workInProgress = _next;1078 return;1079 }1080 if ( (completedWork.mode & ProfileMode) !== NoMode) {1081 // Record the render duration for the fiber that errored.1082 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false); // Include the time spent working on failed children before continuing.1083 var actualDuration = completedWork.actualDuration;1084 var child = completedWork.child;1085 while (child !== null) {1086 actualDuration += child.actualDuration;1087 child = child.sibling;1088 }1089 completedWork.actualDuration = actualDuration;1090 }1091 if (returnFiber !== null) {1092 // Mark the parent fiber as incomplete and clear its effect list.1093 returnFiber.firstEffect = returnFiber.lastEffect = null;1094 returnFiber.flags |= Incomplete;1095 }1096 }1097 var siblingFiber = completedWork.sibling;1098 if (siblingFiber !== null) {1099 // If there is more work to do in this returnFiber, do that next.1100 workInProgress = siblingFiber;1101 return;1102 } // Otherwise, return to the parent1103 completedWork = returnFiber; // Update the next thing we're working on in case something throws.1104 workInProgress = completedWork;1105 } while (completedWork !== null); // We've reached the root.1106 if (workInProgressRootExitStatus === RootIncomplete) {1107 workInProgressRootExitStatus = RootCompleted;1108 }1109 }1110 function resetChildLanes(completedWork) {1111 if ( // TODO: Move this check out of the hot path by moving `resetChildLanes`1112 // to switch statement in `completeWork`.1113 (completedWork.tag === LegacyHiddenComponent || completedWork.tag === OffscreenComponent) && completedWork.memoizedState !== null && !includesSomeLane(subtreeRenderLanes, OffscreenLane) && (completedWork.mode & ConcurrentMode) !== NoLanes) {1114 // The children of this component are hidden. Don't bubble their1115 // expiration times.1116 return;1117 }1118 var newChildLanes = NoLanes; // Bubble up the earliest expiration time.1119 if ( (completedWork.mode & ProfileMode) !== NoMode) {1120 // In profiling mode, resetChildExpirationTime is also used to reset1121 // profiler durations.1122 var actualDuration = completedWork.actualDuration;1123 var treeBaseDuration = completedWork.selfBaseDuration; // When a fiber is cloned, its actualDuration is reset to 0. This value will1124 // only be updated if work is done on the fiber (i.e. it doesn't bailout).1125 // When work is done, it should bubble to the parent's actualDuration. If1126 // the fiber has not been cloned though, (meaning no work was done), then1127 // this value will reflect the amount of time spent working on a previous1128 // render. In that case it should not bubble. We determine whether it was1129 // cloned by comparing the child pointer.1130 var shouldBubbleActualDurations = completedWork.alternate === null || completedWork.child !== completedWork.alternate.child;1131 var child = completedWork.child;1132 while (child !== null) {1133 newChildLanes = mergeLanes(newChildLanes, mergeLanes(child.lanes, child.childLanes));1134 if (shouldBubbleActualDurations) {1135 actualDuration += child.actualDuration;1136 }1137 treeBaseDuration += child.treeBaseDuration;1138 child = child.sibling;1139 }1140 var isTimedOutSuspense = completedWork.tag === SuspenseComponent && completedWork.memoizedState !== null;1141 if (isTimedOutSuspense) {1142 // Don't count time spent in a timed out Suspense subtree as part of the base duration.1143 var primaryChildFragment = completedWork.child;1144 if (primaryChildFragment !== null) {1145 treeBaseDuration -= primaryChildFragment.treeBaseDuration;1146 }1147 }1148 completedWork.actualDuration = actualDuration;1149 completedWork.treeBaseDuration = treeBaseDuration;1150 } else {1151 var _child = completedWork.child;1152 while (_child !== null) {1153 newChildLanes = mergeLanes(newChildLanes, mergeLanes(_child.lanes, _child.childLanes));1154 _child = _child.sibling;1155 }1156 }1157 completedWork.childLanes = newChildLanes;1158 }1159 function commitRoot(root) {1160 var renderPriorityLevel = getCurrentPriorityLevel();1161 runWithPriority$1(ImmediatePriority$1, commitRootImpl.bind(null, root, renderPriorityLevel));1162 return null;1163 }1164 function commitRootImpl(root, renderPriorityLevel) {1165 do {1166 // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which1167 // means `flushPassiveEffects` will sometimes result in additional1168 // passive effects. So we need to keep flushing in a loop until there are1169 // no more pending effects.1170 // TODO: Might be better if `flushPassiveEffects` did not automatically1171 // flush synchronous work at the end, to avoid factoring hazards like this.1172 flushPassiveEffects();1173 } while (rootWithPendingPassiveEffects !== null);1174 flushRenderPhaseStrictModeWarningsInDEV();1175 if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) {1176 {1177 throw Error( "Should not already be working." );1178 }1179 }1180 var finishedWork = root.finishedWork;1181 var lanes = root.finishedLanes;1182 {1183 markCommitStarted(lanes);1184 }1185 if (finishedWork === null) {1186 {1187 markCommitStopped();1188 }1189 return null;1190 }1191 root.finishedWork = null;1192 root.finishedLanes = NoLanes;1193 if (!(finishedWork !== root.current)) {1194 {1195 throw Error( "Cannot commit the same tree as before. This error is likely caused by a bug in React. Please file an issue." );1196 }1197 } // commitRoot never returns a continuation; it always finishes synchronously.1198 // So we can clear these now to allow a new callback to be scheduled.1199 root.callbackNode = null; // Update the first and last pending times on this root. The new first1200 // pending time is whatever is left on the root fiber.1201 var remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);1202 markRootFinished(root, remainingLanes); // Clear already finished discrete updates in case that a later call of1203 // `flushDiscreteUpdates` starts a useless render pass which may cancels1204 // a scheduled timeout.1205 if (rootsWithPendingDiscreteUpdates !== null) {1206 if (!hasDiscreteLanes(remainingLanes) && rootsWithPendingDiscreteUpdates.has(root)) {1207 rootsWithPendingDiscreteUpdates.delete(root);1208 }1209 }1210 if (root === workInProgressRoot) {1211 // We can reset these now that they are finished.1212 workInProgressRoot = null;1213 workInProgress = null;1214 workInProgressRootRenderLanes = NoLanes;1215 } // Get the list of effects.1216 var firstEffect;1217 if (finishedWork.flags > PerformedWork) {1218 // A fiber's effect list consists only of its children, not itself. So if1219 // the root has an effect, we need to add it to the end of the list. The1220 // resulting list is the set that would belong to the root's parent, if it1221 // had one; that is, all the effects in the tree including the root.1222 if (finishedWork.lastEffect !== null) {1223 finishedWork.lastEffect.nextEffect = finishedWork;1224 firstEffect = finishedWork.firstEffect;1225 } else {1226 firstEffect = finishedWork;1227 }1228 } else {1229 // There is no effect on the root.1230 firstEffect = finishedWork.firstEffect;1231 }1232 if (firstEffect !== null) {1233 var prevExecutionContext = executionContext;1234 executionContext |= CommitContext;1235 var prevInteractions = pushInteractions(root); // Reset this to null before calling lifecycles1236 ReactCurrentOwner$2.current = null; // The commit phase is broken into several sub-phases. We do a separate pass1237 // of the effect list for each phase: all mutation effects come before all1238 // layout effects, and so on.1239 // The first phase a "before mutation" phase. We use this phase to read the1240 // state of the host tree right before we mutate it. This is where1241 // getSnapshotBeforeUpdate is called.1242 focusedInstanceHandle = prepareForCommit(root.containerInfo);1243 shouldFireAfterActiveInstanceBlur = false;1244 nextEffect = firstEffect;1245 do {1246 {1247 invokeGuardedCallback(null, commitBeforeMutationEffects, null);1248 if (hasCaughtError()) {1249 if (!(nextEffect !== null)) {1250 {1251 throw Error( "Should be working on an effect." );1252 }1253 }1254 var error = clearCaughtError();1255 captureCommitPhaseError(nextEffect, error);1256 nextEffect = nextEffect.nextEffect;1257 }1258 }1259 } while (nextEffect !== null); // We no longer need to track the active instance fiber1260 focusedInstanceHandle = null;1261 {1262 // Mark the current commit time to be shared by all Profilers in this1263 // batch. This enables them to be grouped later.1264 recordCommitTime();1265 } // The next phase is the mutation phase, where we mutate the host tree.1266 nextEffect = firstEffect;1267 do {1268 {1269 invokeGuardedCallback(null, commitMutationEffects, null, root, renderPriorityLevel);1270 if (hasCaughtError()) {1271 if (!(nextEffect !== null)) {1272 {1273 throw Error( "Should be working on an effect." );1274 }1275 }1276 var _error = clearCaughtError();1277 captureCommitPhaseError(nextEffect, _error);1278 nextEffect = nextEffect.nextEffect;1279 }1280 }1281 } while (nextEffect !== null);1282 resetAfterCommit(root.containerInfo); // The work-in-progress tree is now the current tree. This must come after1283 // the mutation phase, so that the previous tree is still current during1284 // componentWillUnmount, but before the layout phase, so that the finished1285 // work is current during componentDidMount/Update.1286 root.current = finishedWork; // The next phase is the layout phase, where we call effects that read1287 // the host tree after it's been mutated. The idiomatic use case for this is1288 // layout, but class component lifecycles also fire here for legacy reasons.1289 nextEffect = firstEffect;1290 do {1291 {1292 invokeGuardedCallback(null, commitLayoutEffects, null, root, lanes);1293 if (hasCaughtError()) {1294 if (!(nextEffect !== null)) {1295 {1296 throw Error( "Should be working on an effect." );1297 }1298 }1299 var _error2 = clearCaughtError();1300 captureCommitPhaseError(nextEffect, _error2);1301 nextEffect = nextEffect.nextEffect;1302 }1303 }1304 } while (nextEffect !== null);1305 nextEffect = null; // Tell Scheduler to yield at the end of the frame, so the browser has an1306 // opportunity to paint.1307 requestPaint();1308 {1309 popInteractions(prevInteractions);1310 }1311 executionContext = prevExecutionContext;1312 } else {1313 // No effects.1314 root.current = finishedWork; // Measure these anyway so the flamegraph explicitly shows that there were1315 // no effects.1316 // TODO: Maybe there's a better way to report this.1317 {1318 recordCommitTime();1319 }1320 }1321 var rootDidHavePassiveEffects = rootDoesHavePassiveEffects;1322 if (rootDoesHavePassiveEffects) {1323 // This commit has passive effects. Stash a reference to them. But don't1324 // schedule a callback until after flushing layout work.1325 rootDoesHavePassiveEffects = false;1326 rootWithPendingPassiveEffects = root;1327 pendingPassiveEffectsLanes = lanes;1328 pendingPassiveEffectsRenderPriority = renderPriorityLevel;1329 } else {1330 // We are done with the effect chain at this point so let's clear the1331 // nextEffect pointers to assist with GC. If we have passive effects, we'll1332 // clear this in flushPassiveEffects.1333 nextEffect = firstEffect;1334 while (nextEffect !== null) {1335 var nextNextEffect = nextEffect.nextEffect;1336 nextEffect.nextEffect = null;1337 if (nextEffect.flags & Deletion) {1338 detachFiberAfterEffects(nextEffect);1339 }1340 nextEffect = nextNextEffect;1341 }1342 } // Read this again, since an effect might have updated it1343 remainingLanes = root.pendingLanes; // Check if there's remaining work on this root1344 if (remainingLanes !== NoLanes) {1345 {1346 if (spawnedWorkDuringRender !== null) {1347 var expirationTimes = spawnedWorkDuringRender;1348 spawnedWorkDuringRender = null;1349 for (var i = 0; i < expirationTimes.length; i++) {1350 scheduleInteractions(root, expirationTimes[i], root.memoizedInteractions);1351 }1352 }1353 schedulePendingInteractions(root, remainingLanes);1354 }1355 } else {1356 // If there's no remaining work, we can clear the set of already failed1357 // error boundaries.1358 legacyErrorBoundariesThatAlreadyFailed = null;1359 }1360 {1361 if (!rootDidHavePassiveEffects) {1362 // If there are no passive effects, then we can complete the pending interactions.1363 // Otherwise, we'll wait until after the passive effects are flushed.1364 // Wait to do this until after remaining work has been scheduled,1365 // so that we don't prematurely signal complete for interactions when there's e.g. hidden work.1366 finishPendingInteractions(root, lanes);1367 }1368 }1369 if (remainingLanes === SyncLane) {1370 // Count the number of times the root synchronously re-renders without1371 // finishing. If there are too many, it indicates an infinite update loop.1372 if (root === rootWithNestedUpdates) {1373 nestedUpdateCount++;1374 } else {1375 nestedUpdateCount = 0;1376 rootWithNestedUpdates = root;1377 }1378 } else {1379 nestedUpdateCount = 0;1380 }1381 onCommitRoot(finishedWork.stateNode, renderPriorityLevel);1382 {1383 onCommitRoot$1();1384 } // Always call this before exiting `commitRoot`, to ensure that any1385 // additional work on this root is scheduled.1386 ensureRootIsScheduled(root, now());1387 if (hasUncaughtError) {1388 hasUncaughtError = false;1389 var _error3 = firstUncaughtError;1390 firstUncaughtError = null;1391 throw _error3;1392 }1393 if ((executionContext & LegacyUnbatchedContext) !== NoContext) {1394 {1395 markCommitStopped();1396 } // This is a legacy edge case. We just committed the initial mount of1397 // a ReactDOM.render-ed root inside of batchedUpdates. The commit fired1398 // synchronously, but layout updates should be deferred until the end1399 // of the batch.1400 return null;1401 } // If layout work was scheduled, flush it now.1402 flushSyncCallbackQueue();1403 {1404 markCommitStopped();1405 }1406 return null;1407 }1408 function commitBeforeMutationEffects() {1409 while (nextEffect !== null) {1410 var current = nextEffect.alternate;1411 if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {1412 if ((nextEffect.flags & Deletion) !== NoFlags) {1413 if (doesFiberContain(nextEffect, focusedInstanceHandle)) {1414 shouldFireAfterActiveInstanceBlur = true;1415 }1416 } else {1417 // TODO: Move this out of the hot path using a dedicated effect tag.1418 if (nextEffect.tag === SuspenseComponent && isSuspenseBoundaryBeingHidden(current, nextEffect) && doesFiberContain(nextEffect, focusedInstanceHandle)) {1419 shouldFireAfterActiveInstanceBlur = true;1420 }1421 }1422 }1423 var flags = nextEffect.flags;1424 if ((flags & Snapshot) !== NoFlags) {1425 setCurrentFiber(nextEffect);1426 commitBeforeMutationLifeCycles(current, nextEffect);1427 resetCurrentFiber();1428 }1429 if ((flags & Passive) !== NoFlags) {1430 // If there are passive effects, schedule a callback to flush at1431 // the earliest opportunity.1432 if (!rootDoesHavePassiveEffects) {1433 rootDoesHavePassiveEffects = true;1434 scheduleCallback(NormalPriority$1, function () {1435 flushPassiveEffects();1436 return null;1437 });1438 }1439 }1440 nextEffect = nextEffect.nextEffect;1441 }1442 }1443 function commitMutationEffects(root, renderPriorityLevel) {1444 // TODO: Should probably move the bulk of this function to commitWork.1445 while (nextEffect !== null) {1446 setCurrentFiber(nextEffect);1447 var flags = nextEffect.flags;1448 if (flags & ContentReset) {1449 commitResetTextContent(nextEffect);1450 }1451 if (flags & Ref) {1452 var current = nextEffect.alternate;1453 if (current !== null) {1454 commitDetachRef(current);1455 }1456 } // The following switch statement is only concerned about placement,1457 // updates, and deletions. To avoid needing to add a case for every possible1458 // bitmap value, we remove the secondary effects from the effect tag and1459 // switch on that value.1460 var primaryFlags = flags & (Placement | Update | Deletion | Hydrating);1461 switch (primaryFlags) {1462 case Placement:1463 {1464 commitPlacement(nextEffect); // Clear the "placement" from effect tag so that we know that this is1465 // inserted, before any life-cycles like componentDidMount gets called.1466 // TODO: findDOMNode doesn't rely on this any more but isMounted does1467 // and isMounted is deprecated anyway so we should be able to kill this.1468 nextEffect.flags &= ~Placement;1469 break;1470 }1471 case PlacementAndUpdate:1472 {1473 // Placement1474 commitPlacement(nextEffect); // Clear the "placement" from effect tag so that we know that this is1475 // inserted, before any life-cycles like componentDidMount gets called.1476 nextEffect.flags &= ~Placement; // Update1477 var _current = nextEffect.alternate;1478 commitWork(_current, nextEffect);1479 break;1480 }1481 case Hydrating:1482 {1483 nextEffect.flags &= ~Hydrating;1484 break;1485 }1486 case HydratingAndUpdate:1487 {1488 nextEffect.flags &= ~Hydrating; // Update1489 var _current2 = nextEffect.alternate;1490 commitWork(_current2, nextEffect);1491 break;1492 }1493 case Update:1494 {1495 var _current3 = nextEffect.alternate;1496 commitWork(_current3, nextEffect);1497 break;1498 }1499 case Deletion:1500 {1501 commitDeletion(root, nextEffect);1502 break;1503 }1504 }1505 resetCurrentFiber();1506 nextEffect = nextEffect.nextEffect;1507 }1508 }1509 function commitLayoutEffects(root, committedLanes) {1510 {1511 markLayoutEffectsStarted(committedLanes);1512 } // TODO: Should probably move the bulk of this function to commitWork.1513 while (nextEffect !== null) {1514 setCurrentFiber(nextEffect);1515 var flags = nextEffect.flags;1516 if (flags & (Update | Callback)) {1517 var current = nextEffect.alternate;1518 commitLifeCycles(root, current, nextEffect);1519 }1520 {1521 if (flags & Ref) {1522 commitAttachRef(nextEffect);1523 }1524 }1525 resetCurrentFiber();1526 nextEffect = nextEffect.nextEffect;1527 }1528 {1529 markLayoutEffectsStopped();1530 }1531 }1532 function flushPassiveEffects() {1533 // Returns whether passive effects were flushed.1534 if (pendingPassiveEffectsRenderPriority !== NoPriority$1) {1535 var priorityLevel = pendingPassiveEffectsRenderPriority > NormalPriority$1 ? NormalPriority$1 : pendingPassiveEffectsRenderPriority;1536 pendingPassiveEffectsRenderPriority = NoPriority$1;1537 {1538 return runWithPriority$1(priorityLevel, flushPassiveEffectsImpl);1539 }1540 }1541 return false;1542 }1543 function enqueuePendingPassiveHookEffectMount(fiber, effect) {1544 pendingPassiveHookEffectsMount.push(effect, fiber);1545 if (!rootDoesHavePassiveEffects) {1546 rootDoesHavePassiveEffects = true;1547 scheduleCallback(NormalPriority$1, function () {1548 flushPassiveEffects();1549 return null;1550 });1551 }1552 }1553 function enqueuePendingPassiveHookEffectUnmount(fiber, effect) {1554 pendingPassiveHookEffectsUnmount.push(effect, fiber);1555 {1556 fiber.flags |= PassiveUnmountPendingDev;1557 var alternate = fiber.alternate;1558 if (alternate !== null) {1559 alternate.flags |= PassiveUnmountPendingDev;1560 }1561 }1562 if (!rootDoesHavePassiveEffects) {1563 rootDoesHavePassiveEffects = true;1564 scheduleCallback(NormalPriority$1, function () {1565 flushPassiveEffects();1566 return null;1567 });1568 }1569 }1570 function invokePassiveEffectCreate(effect) {1571 var create = effect.create;1572 effect.destroy = create();1573 }1574 function flushPassiveEffectsImpl() {1575 if (rootWithPendingPassiveEffects === null) {1576 return false;1577 }1578 var root = rootWithPendingPassiveEffects;1579 var lanes = pendingPassiveEffectsLanes;1580 rootWithPendingPassiveEffects = null;1581 pendingPassiveEffectsLanes = NoLanes;1582 if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) {1583 {1584 throw Error( "Cannot flush passive effects while already rendering." );1585 }1586 }1587 {1588 markPassiveEffectsStarted(lanes);1589 }1590 {1591 isFlushingPassiveEffects = true;1592 }1593 var prevExecutionContext = executionContext;1594 executionContext |= CommitContext;1595 var prevInteractions = pushInteractions(root); // It's important that ALL pending passive effect destroy functions are called1596 // before ANY passive effect create functions are called.1597 // Otherwise effects in sibling components might interfere with each other.1598 // e.g. a destroy function in one component may unintentionally override a ref1599 // value set by a create function in another component.1600 // Layout effects have the same constraint.1601 // First pass: Destroy stale passive effects.1602 var unmountEffects = pendingPassiveHookEffectsUnmount;1603 pendingPassiveHookEffectsUnmount = [];1604 for (var i = 0; i < unmountEffects.length; i += 2) {1605 var _effect = unmountEffects[i];1606 var fiber = unmountEffects[i + 1];1607 var destroy = _effect.destroy;1608 _effect.destroy = undefined;1609 {1610 fiber.flags &= ~PassiveUnmountPendingDev;1611 var alternate = fiber.alternate;1612 if (alternate !== null) {1613 alternate.flags &= ~PassiveUnmountPendingDev;1614 }1615 }1616 if (typeof destroy === 'function') {1617 {1618 setCurrentFiber(fiber);1619 {1620 invokeGuardedCallback(null, destroy, null);1621 }1622 if (hasCaughtError()) {1623 if (!(fiber !== null)) {1624 {1625 throw Error( "Should be working on an effect." );1626 }1627 }1628 var error = clearCaughtError();1629 captureCommitPhaseError(fiber, error);1630 }1631 resetCurrentFiber();1632 }1633 }1634 } // Second pass: Create new passive effects.1635 var mountEffects = pendingPassiveHookEffectsMount;1636 pendingPassiveHookEffectsMount = [];1637 for (var _i = 0; _i < mountEffects.length; _i += 2) {1638 var _effect2 = mountEffects[_i];1639 var _fiber = mountEffects[_i + 1];1640 {1641 setCurrentFiber(_fiber);1642 {1643 invokeGuardedCallback(null, invokePassiveEffectCreate, null, _effect2);1644 }1645 if (hasCaughtError()) {1646 if (!(_fiber !== null)) {1647 {1648 throw Error( "Should be working on an effect." );1649 }1650 }1651 var _error4 = clearCaughtError();1652 captureCommitPhaseError(_fiber, _error4);1653 }1654 resetCurrentFiber();1655 }1656 } // Note: This currently assumes there are no passive effects on the root fiber1657 // because the root is not part of its own effect list.1658 // This could change in the future.1659 var effect = root.current.firstEffect;1660 while (effect !== null) {1661 var nextNextEffect = effect.nextEffect; // Remove nextEffect pointer to assist GC1662 effect.nextEffect = null;1663 if (effect.flags & Deletion) {1664 detachFiberAfterEffects(effect);1665 }1666 effect = nextNextEffect;1667 }1668 {1669 popInteractions(prevInteractions);1670 finishPendingInteractions(root, lanes);1671 }1672 {1673 isFlushingPassiveEffects = false;1674 }1675 {1676 markPassiveEffectsStopped();1677 }1678 executionContext = prevExecutionContext;1679 flushSyncCallbackQueue(); // If additional passive effects were scheduled, increment a counter. If this1680 // exceeds the limit, we'll fire a warning.1681 nestedPassiveUpdateCount = rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;1682 return true;1683 }1684 function isAlreadyFailedLegacyErrorBoundary(instance) {1685 return legacyErrorBoundariesThatAlreadyFailed !== null && legacyErrorBoundariesThatAlreadyFailed.has(instance);1686 }1687 function markLegacyErrorBoundaryAsFailed(instance) {1688 if (legacyErrorBoundariesThatAlreadyFailed === null) {1689 legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);1690 } else {1691 legacyErrorBoundariesThatAlreadyFailed.add(instance);1692 }1693 }1694 function prepareToThrowUncaughtError(error) {1695 if (!hasUncaughtError) {1696 hasUncaughtError = true;1697 firstUncaughtError = error;1698 }1699 }1700 var onUncaughtError = prepareToThrowUncaughtError;1701 function captureCommitPhaseErrorOnRoot(rootFiber, sourceFiber, error) {1702 var errorInfo = createCapturedValue(error, sourceFiber);1703 var update = createRootErrorUpdate(rootFiber, errorInfo, SyncLane);1704 enqueueUpdate(rootFiber, update);1705 var eventTime = requestEventTime();1706 var root = markUpdateLaneFromFiberToRoot(rootFiber, SyncLane);1707 if (root !== null) {1708 markRootUpdated(root, SyncLane, eventTime);1709 ensureRootIsScheduled(root, eventTime);1710 schedulePendingInteractions(root, SyncLane);1711 }1712 }1713 function captureCommitPhaseError(sourceFiber, error) {1714 if (sourceFiber.tag === HostRoot) {1715 // Error was thrown at the root. There is no parent, so the root1716 // itself should capture it.1717 captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);1718 return;1719 }1720 var fiber = sourceFiber.return;1721 while (fiber !== null) {1722 if (fiber.tag === HostRoot) {1723 captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);1724 return;1725 } else if (fiber.tag === ClassComponent) {1726 var ctor = fiber.type;1727 var instance = fiber.stateNode;1728 if (typeof ctor.getDerivedStateFromError === 'function' || typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance)) {1729 var errorInfo = createCapturedValue(error, sourceFiber);1730 var update = createClassErrorUpdate(fiber, errorInfo, SyncLane);1731 enqueueUpdate(fiber, update);1732 var eventTime = requestEventTime();1733 var root = markUpdateLaneFromFiberToRoot(fiber, SyncLane);1734 if (root !== null) {1735 markRootUpdated(root, SyncLane, eventTime);1736 ensureRootIsScheduled(root, eventTime);1737 schedulePendingInteractions(root, SyncLane);1738 } else {1739 // This component has already been unmounted.1740 // We can't schedule any follow up work for the root because the fiber is already unmounted,1741 // but we can still call the log-only boundary so the error isn't swallowed.1742 //1743 // TODO This is only a temporary bandaid for the old reconciler fork.1744 // We can delete this special case once the new fork is merged.1745 if (typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance)) {1746 try {1747 instance.componentDidCatch(error, errorInfo);1748 } catch (errorToIgnore) {// TODO Ignore this error? Rethrow it?1749 // This is kind of an edge case.1750 }1751 }1752 }1753 return;1754 }1755 }1756 fiber = fiber.return;1757 }1758 }1759 function pingSuspendedRoot(root, wakeable, pingedLanes) {1760 var pingCache = root.pingCache;1761 if (pingCache !== null) {1762 // The wakeable resolved, so we no longer need to memoize, because it will1763 // never be thrown again.1764 pingCache.delete(wakeable);1765 }1766 var eventTime = requestEventTime();1767 markRootPinged(root, pingedLanes);1768 if (workInProgressRoot === root && isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)) {1769 // Received a ping at the same priority level at which we're currently1770 // rendering. We might want to restart this render. This should mirror1771 // the logic of whether or not a root suspends once it completes.1772 // TODO: If we're rendering sync either due to Sync, Batched or expired,1773 // we should probably never restart.1774 // If we're suspended with delay, or if it's a retry, we'll always suspend1775 // so we can always restart.1776 if (workInProgressRootExitStatus === RootSuspendedWithDelay || workInProgressRootExitStatus === RootSuspended && includesOnlyRetries(workInProgressRootRenderLanes) && now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS) {1777 // Restart from the root.1778 prepareFreshStack(root, NoLanes);1779 } else {1780 // Even though we can't restart right now, we might get an1781 // opportunity later. So we mark this render as having a ping.1782 workInProgressRootPingedLanes = mergeLanes(workInProgressRootPingedLanes, pingedLanes);1783 }1784 }1785 ensureRootIsScheduled(root, eventTime);1786 schedulePendingInteractions(root, pingedLanes);1787 }1788 function retryTimedOutBoundary(boundaryFiber, retryLane) {1789 // The boundary fiber (a Suspense component or SuspenseList component)1790 // previously was rendered in its fallback state. One of the promises that1791 // suspended it has resolved, which means at least part of the tree was1792 // likely unblocked. Try rendering again, at a new expiration time.1793 if (retryLane === NoLane) {1794 retryLane = requestRetryLane(boundaryFiber);1795 } // TODO: Special case idle priority?1796 var eventTime = requestEventTime();1797 var root = markUpdateLaneFromFiberToRoot(boundaryFiber, retryLane);1798 if (root !== null) {1799 markRootUpdated(root, retryLane, eventTime);1800 ensureRootIsScheduled(root, eventTime);1801 schedulePendingInteractions(root, retryLane);1802 }1803 }1804 function retryDehydratedSuspenseBoundary(boundaryFiber) {1805 var suspenseState = boundaryFiber.memoizedState;1806 var retryLane = NoLane;1807 if (suspenseState !== null) {1808 retryLane = suspenseState.retryLane;1809 }1810 retryTimedOutBoundary(boundaryFiber, retryLane);1811 }1812 function resolveRetryWakeable(boundaryFiber, wakeable) {1813 var retryLane = NoLane; // Default1814 var retryCache;1815 {1816 switch (boundaryFiber.tag) {1817 case SuspenseComponent:1818 retryCache = boundaryFiber.stateNode;1819 var suspenseState = boundaryFiber.memoizedState;1820 if (suspenseState !== null) {1821 retryLane = suspenseState.retryLane;1822 }1823 break;1824 case SuspenseListComponent:1825 retryCache = boundaryFiber.stateNode;1826 break;1827 default:1828 {1829 {1830 throw Error( "Pinged unknown suspense boundary type. This is probably a bug in React." );1831 }1832 }1833 }1834 }1835 if (retryCache !== null) {1836 // The wakeable resolved, so we no longer need to memoize, because it will1837 // never be thrown again.1838 retryCache.delete(wakeable);1839 }1840 retryTimedOutBoundary(boundaryFiber, retryLane);1841 } // Computes the next Just Noticeable Difference (JND) boundary.1842 // The theory is that a person can't tell the difference between small differences in time.1843 // Therefore, if we wait a bit longer than necessary that won't translate to a noticeable1844 // difference in the experience. However, waiting for longer might mean that we can avoid1845 // showing an intermediate loading state. The longer we have already waited, the harder it1846 // is to tell small differences in time. Therefore, the longer we've already waited,1847 // the longer we can wait additionally. At some point we have to give up though.1848 // We pick a train model where the next boundary commits at a consistent schedule.1849 // These particular numbers are vague estimates. We expect to adjust them based on research.1850 function jnd(timeElapsed) {1851 return timeElapsed < 120 ? 120 : timeElapsed < 480 ? 480 : timeElapsed < 1080 ? 1080 : timeElapsed < 1920 ? 1920 : timeElapsed < 3000 ? 3000 : timeElapsed < 4320 ? 4320 : ceil(timeElapsed / 1960) * 1960;1852 }1853 function checkForNestedUpdates() {1854 if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {1855 nestedUpdateCount = 0;1856 rootWithNestedUpdates = null;1857 {1858 {1859 throw Error( "Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops." );1860 }1861 }1862 }1863 {1864 if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {1865 nestedPassiveUpdateCount = 0;1866 error('Maximum update depth exceeded. This can happen when a component ' + "calls setState inside useEffect, but useEffect either doesn't " + 'have a dependency array, or one of the dependencies changes on ' + 'every render.');1867 }1868 }1869 }1870 function flushRenderPhaseStrictModeWarningsInDEV() {1871 {1872 ReactStrictModeWarnings.flushLegacyContextWarning();1873 {1874 ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();1875 }1876 }1877 }1878 var didWarnStateUpdateForNotYetMountedComponent = null;1879 function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber) {1880 {1881 if ((executionContext & RenderContext) !== NoContext) {1882 // We let the other warning about render phase updates deal with this one.1883 return;1884 }1885 if (!(fiber.mode & (BlockingMode | ConcurrentMode))) {1886 return;1887 }1888 var tag = fiber.tag;1889 if (tag !== IndeterminateComponent && tag !== HostRoot && tag !== ClassComponent && tag !== FunctionComponent && tag !== ForwardRef && tag !== MemoComponent && tag !== SimpleMemoComponent && tag !== Block) {1890 // Only warn for user-defined components, not internal ones like Suspense.1891 return;1892 } // We show the whole stack but dedupe on the top component's name because1893 // the problematic code almost always lies inside that component.1894 var componentName = getComponentName(fiber.type) || 'ReactComponent';1895 if (didWarnStateUpdateForNotYetMountedComponent !== null) {1896 if (didWarnStateUpdateForNotYetMountedComponent.has(componentName)) {1897 return;1898 }1899 didWarnStateUpdateForNotYetMountedComponent.add(componentName);1900 } else {1901 didWarnStateUpdateForNotYetMountedComponent = new Set([componentName]);1902 }1903 var previousFiber = current;1904 try {1905 setCurrentFiber(fiber);1906 error("Can't perform a React state update on a component that hasn't mounted yet. " + 'This indicates that you have a side-effect in your render function that ' + 'asynchronously later calls tries to update the component. Move this work to ' + 'useEffect instead.');1907 } finally {1908 if (previousFiber) {1909 setCurrentFiber(fiber);1910 } else {1911 resetCurrentFiber();1912 }1913 }1914 }1915 }1916 var didWarnStateUpdateForUnmountedComponent = null;1917 function warnAboutUpdateOnUnmountedFiberInDEV(fiber) {1918 {1919 var tag = fiber.tag;1920 if (tag !== HostRoot && tag !== ClassComponent && tag !== FunctionComponent && tag !== ForwardRef && tag !== MemoComponent && tag !== SimpleMemoComponent && tag !== Block) {1921 // Only warn for user-defined components, not internal ones like Suspense.1922 return;1923 } // If there are pending passive effects unmounts for this Fiber,1924 // we can assume that they would have prevented this update.1925 if ((fiber.flags & PassiveUnmountPendingDev) !== NoFlags) {1926 return;1927 } // We show the whole stack but dedupe on the top component's name because1928 // the problematic code almost always lies inside that component.1929 var componentName = getComponentName(fiber.type) || 'ReactComponent';1930 if (didWarnStateUpdateForUnmountedComponent !== null) {1931 if (didWarnStateUpdateForUnmountedComponent.has(componentName)) {...

Full Screen

Full Screen

ReactFiberScheduler.js

Source:ReactFiberScheduler.js Github

copy

Full Screen

...288 checkForNestedUpdates();289 warnAboutInvalidUpdatesOnClassComponentsInDEV(fiber);290 const root = markUpdateTimeFromFiberToRoot(fiber, expirationTime);291 if (root === null) {292 warnAboutUpdateOnUnmountedFiberInDEV(fiber);293 return;294 }295 root.pingTime = NoWork;296 checkForInterruption(fiber, expirationTime);297 recordScheduleUpdate();298 if (expirationTime === Sync) {299 if (workPhase === LegacyUnbatchedPhase) {300 // This is a legacy edge case. The initial mount of a ReactDOM.render-ed301 // root inside of batchedUpdates should be synchronous, but layout updates302 // should be deferred until the end of the batch.303 let callback = renderRoot(root, Sync, true);304 while (callback !== null) {305 callback = callback(true);306 }307 } else {308 scheduleCallbackForRoot(root, ImmediatePriority, Sync);309 if (workPhase === NotWorking) {310 // Flush the synchronous work now, wnless we're already working or inside311 // a batch. This is intentionally inside scheduleUpdateOnFiber instead of312 // scheduleCallbackForFiber to preserve the ability to schedule a callback313 // without immediately flushing it. We only do this for user-initated314 // updates, to preserve historical behavior of sync mode.315 flushImmediateQueue();316 }317 }318 } else {319 // TODO: computeExpirationForFiber also reads the priority. Pass the320 // priority as an argument to that function and this one.321 const priorityLevel = getCurrentPriorityLevel();322 if (priorityLevel === UserBlockingPriority) {323 // This is the result of a discrete event. Track the lowest priority324 // discrete update per root so we can flush them early, if needed.325 if (rootsWithPendingDiscreteUpdates === null) {326 rootsWithPendingDiscreteUpdates = new Map([[root, expirationTime]]);327 } else {328 const lastDiscreteTime = rootsWithPendingDiscreteUpdates.get(root);329 if (330 lastDiscreteTime === undefined ||331 lastDiscreteTime > expirationTime332 ) {333 rootsWithPendingDiscreteUpdates.set(root, expirationTime);334 }335 }336 }337 scheduleCallbackForRoot(root, priorityLevel, expirationTime);338 }339}340export const scheduleWork = scheduleUpdateOnFiber;341// This is split into a separate function so we can mark a fiber with pending342// work without treating it as a typical update that originates from an event;343// e.g. retrying a Suspense boundary isn't an update, but it does schedule work344// on a fiber.345function markUpdateTimeFromFiberToRoot(fiber, expirationTime) {346 // Update the source fiber's expiration time347 if (fiber.expirationTime < expirationTime) {348 fiber.expirationTime = expirationTime;349 }350 let alternate = fiber.alternate;351 if (alternate !== null && alternate.expirationTime < expirationTime) {352 alternate.expirationTime = expirationTime;353 }354 // Walk the parent path to the root and update the child expiration time.355 let node = fiber.return;356 let root = null;357 if (node === null && fiber.tag === HostRoot) {358 root = fiber.stateNode;359 } else {360 while (node !== null) {361 alternate = node.alternate;362 if (node.childExpirationTime < expirationTime) {363 node.childExpirationTime = expirationTime;364 if (365 alternate !== null &&366 alternate.childExpirationTime < expirationTime367 ) {368 alternate.childExpirationTime = expirationTime;369 }370 } else if (371 alternate !== null &&372 alternate.childExpirationTime < expirationTime373 ) {374 alternate.childExpirationTime = expirationTime;375 }376 if (node.return === null && node.tag === HostRoot) {377 root = node.stateNode;378 break;379 }380 node = node.return;381 }382 }383 if (root !== null) {384 // Update the first and last pending expiration times in this root385 const firstPendingTime = root.firstPendingTime;386 if (expirationTime > firstPendingTime) {387 root.firstPendingTime = expirationTime;388 }389 const lastPendingTime = root.lastPendingTime;390 if (lastPendingTime === NoWork || expirationTime < lastPendingTime) {391 root.lastPendingTime = expirationTime;392 }393 }394 return root;395}396// Use this function, along with runRootCallback, to ensure that only a single397// callback per root is scheduled. It's still possible to call renderRoot398// directly, but scheduling via this function helps avoid excessive callbacks.399// It works by storing the callback node and expiration time on the root. When a400// new callback comes in, it compares the expiration time to determine if it401// should cancel the previous one. It also relies on commitRoot scheduling a402// callback to render the next level, because that means we don't need a403// separate callback per expiration time.404function scheduleCallbackForRoot(405 root: FiberRoot,406 priorityLevel: ReactPriorityLevel,407 expirationTime: ExpirationTime,408) {409 const existingCallbackExpirationTime = root.callbackExpirationTime;410 if (existingCallbackExpirationTime < expirationTime) {411 // New callback has higher priority than the existing one.412 const existingCallbackNode = root.callbackNode;413 if (existingCallbackNode !== null) {414 cancelCallback(existingCallbackNode);415 }416 root.callbackExpirationTime = expirationTime;417 let options = null;418 if (expirationTime !== Sync && expirationTime !== Never) {419 let timeout = expirationTimeToMs(expirationTime) - now();420 if (timeout > 5000) {421 // Sanity check. Should never take longer than 5 seconds.422 // TODO: Add internal warning?423 timeout = 5000;424 }425 options = {timeout};426 }427 root.callbackNode = scheduleCallback(428 priorityLevel,429 runRootCallback.bind(430 null,431 root,432 renderRoot.bind(null, root, expirationTime),433 ),434 options,435 );436 if (437 enableUserTimingAPI &&438 expirationTime !== Sync &&439 workPhase !== RenderPhase &&440 workPhase !== CommitPhase441 ) {442 // Scheduled an async callback, and we're not already working. Add an443 // entry to the flamegraph that shows we're waiting for a callback444 // to fire.445 startRequestCallbackTimer();446 }447 }448 // Add the current set of interactions to the pending set associated with449 // this root.450 schedulePendingInteraction(root, expirationTime);451}452function runRootCallback(root, callback, isSync) {453 const prevCallbackNode = root.callbackNode;454 let continuation = null;455 try {456 continuation = callback(isSync);457 if (continuation !== null) {458 return runRootCallback.bind(null, root, continuation);459 } else {460 return null;461 }462 } finally {463 // If the callback exits without returning a continuation, remove the464 // corresponding callback node from the root. Unless the callback node465 // has changed, which implies that it was already cancelled by a high466 // priority update.467 if (continuation === null && prevCallbackNode === root.callbackNode) {468 root.callbackNode = null;469 root.callbackExpirationTime = NoWork;470 }471 }472}473export function flushRoot(root: FiberRoot, expirationTime: ExpirationTime) {474 if (workPhase === RenderPhase || workPhase === CommitPhase) {475 invariant(476 false,477 'work.commit(): Cannot commit while already rendering. This likely ' +478 'means you attempted to commit from inside a lifecycle method.',479 );480 }481 scheduleCallback(482 ImmediatePriority,483 renderRoot.bind(null, root, expirationTime),484 );485 flushImmediateQueue();486}487export function flushInteractiveUpdates() {488 if (workPhase === RenderPhase || workPhase === CommitPhase) {489 // Can't synchronously flush interactive updates if React is already490 // working. This is currently a no-op.491 // TODO: Should we fire a warning? This happens if you synchronously invoke492 // an input event inside an effect, like with `element.click()`.493 return;494 }495 flushPendingDiscreteUpdates();496}497function resolveLocksOnRoot(root: FiberRoot, expirationTime: ExpirationTime) {498 const firstBatch = root.firstBatch;499 if (500 firstBatch !== null &&501 firstBatch._defer &&502 firstBatch._expirationTime >= expirationTime503 ) {504 root.finishedWork = root.current.alternate;505 root.pendingCommitExpirationTime = expirationTime;506 scheduleCallback(NormalPriority, () => {507 firstBatch._onComplete();508 return null;509 });510 return true;511 } else {512 return false;513 }514}515export function deferredUpdates<A>(fn: () => A): A {516 // TODO: Remove in favor of Scheduler.next517 return runWithPriority(NormalPriority, fn);518}519export function interactiveUpdates<A, B, C, R>(520 fn: (A, B, C) => R,521 a: A,522 b: B,523 c: C,524): R {525 if (workPhase === NotWorking) {526 // TODO: Remove this call. Instead of doing this automatically, the caller527 // should explicitly call flushInteractiveUpdates.528 flushPendingDiscreteUpdates();529 }530 return runWithPriority(UserBlockingPriority, fn.bind(null, a, b, c));531}532export function syncUpdates<A, B, C, R>(533 fn: (A, B, C) => R,534 a: A,535 b: B,536 c: C,537): R {538 return runWithPriority(ImmediatePriority, fn.bind(null, a, b, c));539}540function flushPendingDiscreteUpdates() {541 if (rootsWithPendingDiscreteUpdates !== null) {542 // For each root with pending discrete updates, schedule a callback to543 // immediately flush them.544 const roots = rootsWithPendingDiscreteUpdates;545 rootsWithPendingDiscreteUpdates = null;546 roots.forEach((expirationTime, root) => {547 scheduleCallback(548 ImmediatePriority,549 renderRoot.bind(null, root, expirationTime),550 );551 });552 // Now flush the immediate queue.553 flushImmediateQueue();554 }555}556export function batchedUpdates<A, R>(fn: A => R, a: A): R {557 if (workPhase !== NotWorking) {558 // We're already working, or inside a batch, so batchedUpdates is a no-op.559 return fn(a);560 }561 workPhase = BatchedPhase;562 try {563 return fn(a);564 } finally {565 workPhase = NotWorking;566 // Flush the immediate callbacks that were scheduled during this batch567 flushImmediateQueue();568 }569}570export function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {571 if (workPhase !== BatchedPhase && workPhase !== FlushSyncPhase) {572 // We're not inside batchedUpdates or flushSync, so unbatchedUpdates is573 // a no-op.574 return fn(a);575 }576 const prevWorkPhase = workPhase;577 workPhase = LegacyUnbatchedPhase;578 try {579 return fn(a);580 } finally {581 workPhase = prevWorkPhase;582 }583}584export function flushSync<A, R>(fn: A => R, a: A): R {585 if (workPhase === RenderPhase || workPhase === CommitPhase) {586 invariant(587 false,588 'flushSync was called from inside a lifecycle method. It cannot be ' +589 'called when React is already rendering.',590 );591 }592 const prevWorkPhase = workPhase;593 workPhase = FlushSyncPhase;594 try {595 return runWithPriority(ImmediatePriority, fn.bind(null, a));596 } finally {597 workPhase = prevWorkPhase;598 // Flush the immediate callbacks that were scheduled during this batch.599 // Note that this will happen even if batchedUpdates is higher up600 // the stack.601 flushImmediateQueue();602 }603}604export function flushControlled(fn: () => mixed): void {605 const prevWorkPhase = workPhase;606 workPhase = BatchedPhase;607 try {608 runWithPriority(ImmediatePriority, fn);609 } finally {610 workPhase = prevWorkPhase;611 if (workPhase === NotWorking) {612 // Flush the immediate callbacks that were scheduled during this batch613 flushImmediateQueue();614 }615 }616}617function prepareFreshStack(root, expirationTime) {618 root.pendingCommitExpirationTime = NoWork;619 const timeoutHandle = root.timeoutHandle;620 if (timeoutHandle !== noTimeout) {621 // The root previous suspended and scheduled a timeout to commit a fallback622 // state. Now that we have additional work, cancel the timeout.623 root.timeoutHandle = noTimeout;624 // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above625 cancelTimeout(timeoutHandle);626 }627 if (workInProgress !== null) {628 let interruptedWork = workInProgress.return;629 while (interruptedWork !== null) {630 unwindInterruptedWork(interruptedWork);631 interruptedWork = interruptedWork.return;632 }633 }634 workInProgressRoot = root;635 workInProgress = createWorkInProgress(root.current, null, expirationTime);636 renderExpirationTime = expirationTime;637 workInProgressRootExitStatus = RootIncomplete;638 workInProgressRootMostRecentEventTime = Sync;639 if (__DEV__) {640 ReactStrictModeWarnings.discardPendingWarnings();641 }642}643function renderRoot(644 root: FiberRoot,645 expirationTime: ExpirationTime,646 isSync: boolean,647): SchedulerCallback | null {648 invariant(649 workPhase !== RenderPhase && workPhase !== CommitPhase,650 'Should not already be working.',651 );652 if (enableUserTimingAPI && expirationTime !== Sync) {653 const didExpire = isSync;654 stopRequestCallbackTimer(didExpire);655 }656 if (root.firstPendingTime < expirationTime) {657 // If there's no work left at this expiration time, exit immediately. This658 // happens when multiple callbacks are scheduled for a single root, but an659 // earlier callback flushes the work of a later one.660 return null;661 }662 if (root.pendingCommitExpirationTime === expirationTime) {663 // There's already a pending commit at this expiration time.664 root.pendingCommitExpirationTime = NoWork;665 return commitRoot.bind(null, root, expirationTime);666 }667 flushPassiveEffects();668 // If the root or expiration time have changed, throw out the existing stack669 // and prepare a fresh one. Otherwise we'll continue where we left off.670 if (root !== workInProgressRoot || expirationTime !== renderExpirationTime) {671 prepareFreshStack(root, expirationTime);672 startWorkOnPendingInteraction(root, expirationTime);673 }674 // If we have a work-in-progress fiber, it means there's still work to do675 // in this root.676 if (workInProgress !== null) {677 const prevWorkPhase = workPhase;678 workPhase = RenderPhase;679 let prevDispatcher = ReactCurrentDispatcher.current;680 if (prevDispatcher === null) {681 // The React isomorphic package does not include a default dispatcher.682 // Instead the first renderer will lazily attach one, in order to give683 // nicer error messages.684 prevDispatcher = ContextOnlyDispatcher;685 }686 ReactCurrentDispatcher.current = ContextOnlyDispatcher;687 let prevInteractions: Set<Interaction> | null = null;688 if (enableSchedulerTracing) {689 prevInteractions = __interactionsRef.current;690 __interactionsRef.current = root.memoizedInteractions;691 }692 startWorkLoopTimer(workInProgress);693 // TODO: Fork renderRoot into renderRootSync and renderRootAsync694 if (isSync) {695 if (expirationTime !== Sync) {696 // An async update expired. There may be other expired updates on697 // this root. We should render all the expired work in a698 // single batch.699 const currentTime = requestCurrentTime();700 if (currentTime < expirationTime) {701 // Restart at the current time.702 workPhase = prevWorkPhase;703 resetContextDependencies();704 ReactCurrentDispatcher.current = prevDispatcher;705 if (enableSchedulerTracing) {706 __interactionsRef.current = ((prevInteractions: any): Set<707 Interaction,708 >);709 }710 return renderRoot.bind(null, root, currentTime);711 }712 }713 } else {714 // Since we know we're in a React event, we can clear the current715 // event time. The next update will compute a new event time.716 currentEventTime = NoWork;717 }718 do {719 try {720 if (isSync) {721 workLoopSync();722 } else {723 workLoop();724 }725 break;726 } catch (thrownValue) {727 // Reset module-level state that was set during the render phase.728 resetContextDependencies();729 resetHooks();730 const sourceFiber = workInProgress;731 if (sourceFiber === null || sourceFiber.return === null) {732 // Expected to be working on a non-root fiber. This is a fatal error733 // because there's no ancestor that can handle it; the root is734 // supposed to capture all errors that weren't caught by an error735 // boundary.736 prepareFreshStack(root, expirationTime);737 workPhase = prevWorkPhase;738 throw thrownValue;739 }740 if (enableProfilerTimer && sourceFiber.mode & ProfileMode) {741 // Record the time spent rendering before an error was thrown. This742 // avoids inaccurate Profiler durations in the case of a743 // suspended render.744 stopProfilerTimerIfRunningAndRecordDelta(sourceFiber, true);745 }746 const returnFiber = sourceFiber.return;747 throwException(748 root,749 returnFiber,750 sourceFiber,751 thrownValue,752 renderExpirationTime,753 );754 workInProgress = completeUnitOfWork(sourceFiber);755 }756 } while (true);757 workPhase = prevWorkPhase;758 resetContextDependencies();759 ReactCurrentDispatcher.current = prevDispatcher;760 if (enableSchedulerTracing) {761 __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);762 }763 if (workInProgress !== null) {764 // There's still work left over. Return a continuation.765 stopInterruptedWorkLoopTimer();766 if (expirationTime !== Sync) {767 startRequestCallbackTimer();768 }769 return renderRoot.bind(null, root, expirationTime);770 }771 }772 // We now have a consistent tree. The next step is either to commit it, or, if773 // something suspended, wait to commit it after a timeout.774 stopFinishedWorkLoopTimer();775 const isLocked = resolveLocksOnRoot(root, expirationTime);776 if (isLocked) {777 // This root has a lock that prevents it from committing. Exit. If we begin778 // work on the root again, without any intervening updates, it will finish779 // without doing additional work.780 return null;781 }782 // Set this to null to indicate there's no in-progress render.783 workInProgressRoot = null;784 switch (workInProgressRootExitStatus) {785 case RootIncomplete: {786 invariant(false, 'Should have a work-in-progress.');787 }788 // Flow knows about invariant, so it compains if I add a break statement,789 // but eslint doesn't know about invariant, so it complains if I do.790 // eslint-disable-next-line no-fallthrough791 case RootErrored: {792 // An error was thrown. First check if there is lower priority work793 // scheduled on this root.794 const lastPendingTime = root.lastPendingTime;795 if (root.lastPendingTime < expirationTime) {796 // There's lower priority work. Before raising the error, try rendering797 // at the lower priority to see if it fixes it. Use a continuation to798 // maintain the existing priority and position in the queue.799 return renderRoot.bind(null, root, lastPendingTime);800 }801 if (!isSync) {802 // If we're rendering asynchronously, it's possible the error was803 // caused by tearing due to a mutation during an event. Try rendering804 // one more time without yiedling to events.805 prepareFreshStack(root, expirationTime);806 scheduleCallback(807 ImmediatePriority,808 renderRoot.bind(null, root, expirationTime),809 );810 return null;811 }812 // If we're already rendering synchronously, commit the root in its813 // errored state.814 return commitRoot.bind(null, root, expirationTime);815 }816 case RootSuspended: {817 if (!isSync) {818 const lastPendingTime = root.lastPendingTime;819 if (root.lastPendingTime < expirationTime) {820 // There's lower priority work. It might be unsuspended. Try rendering821 // at that level.822 return renderRoot.bind(null, root, lastPendingTime);823 }824 // If workInProgressRootMostRecentEventTime is Sync, that means we didn't825 // track any event times. That can happen if we retried but nothing switched826 // from fallback to content. There's no reason to delay doing no work.827 if (workInProgressRootMostRecentEventTime !== Sync) {828 let msUntilTimeout = computeMsUntilTimeout(829 workInProgressRootMostRecentEventTime,830 expirationTime,831 );832 // Don't bother with a very short suspense time.833 if (msUntilTimeout > 10) {834 // The render is suspended, it hasn't timed out, and there's no lower835 // priority work to do. Instead of committing the fallback836 // immediately, wait for more data to arrive.837 root.timeoutHandle = scheduleTimeout(838 commitRoot.bind(null, root, expirationTime),839 msUntilTimeout,840 );841 return null;842 }843 }844 }845 // The work expired. Commit immediately.846 return commitRoot.bind(null, root, expirationTime);847 }848 case RootCompleted: {849 // The work completed. Ready to commit.850 return commitRoot.bind(null, root, expirationTime);851 }852 default: {853 invariant(false, 'Unknown root exit status.');854 }855 }856}857export function markRenderEventTime(expirationTime: ExpirationTime): void {858 if (expirationTime < workInProgressRootMostRecentEventTime) {859 workInProgressRootMostRecentEventTime = expirationTime;860 }861}862export function renderDidSuspend(): void {863 if (workInProgressRootExitStatus === RootIncomplete) {864 workInProgressRootExitStatus = RootSuspended;865 }866}867export function renderDidError() {868 if (869 workInProgressRootExitStatus === RootIncomplete ||870 workInProgressRootExitStatus === RootSuspended871 ) {872 workInProgressRootExitStatus = RootErrored;873 }874}875function inferTimeFromExpirationTime(expirationTime: ExpirationTime): number {876 // We don't know exactly when the update was scheduled, but we can infer an877 // approximate start time from the expiration time.878 const earliestExpirationTimeMs = expirationTimeToMs(expirationTime);879 return earliestExpirationTimeMs - LOW_PRIORITY_EXPIRATION;880}881function workLoopSync() {882 // Already timed out, so perform work without checking if we need to yield.883 while (workInProgress !== null) {884 workInProgress = performUnitOfWork(workInProgress);885 }886}887function workLoop() {888 // Perform work until Scheduler asks us to yield889 while (workInProgress !== null && !shouldYield()) {890 workInProgress = performUnitOfWork(workInProgress);891 }892}893function performUnitOfWork(unitOfWork: Fiber): Fiber | null {894 // The current, flushed, state of this fiber is the alternate. Ideally895 // nothing should rely on this, but relying on it here means that we don't896 // need an additional field on the work in progress.897 const current = unitOfWork.alternate;898 startWorkTimer(unitOfWork);899 setCurrentDebugFiberInDEV(unitOfWork);900 let next;901 if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoContext) {902 startProfilerTimer(unitOfWork);903 next = beginWork(current, unitOfWork, renderExpirationTime);904 stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);905 } else {906 next = beginWork(current, unitOfWork, renderExpirationTime);907 }908 resetCurrentDebugFiberInDEV();909 unitOfWork.memoizedProps = unitOfWork.pendingProps;910 if (next === null) {911 // If this doesn't spawn new work, complete the current work.912 next = completeUnitOfWork(unitOfWork);913 }914 ReactCurrentOwner.current = null;915 return next;916}917function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {918 // Attempt to complete the current unit of work, then move to the next919 // sibling. If there are no more siblings, return to the parent fiber.920 workInProgress = unitOfWork;921 do {922 // The current, flushed, state of this fiber is the alternate. Ideally923 // nothing should rely on this, but relying on it here means that we don't924 // need an additional field on the work in progress.925 const current = workInProgress.alternate;926 const returnFiber = workInProgress.return;927 // Check if the work completed or if something threw.928 if ((workInProgress.effectTag & Incomplete) === NoEffect) {929 setCurrentDebugFiberInDEV(workInProgress);930 let next;931 if (932 !enableProfilerTimer ||933 (workInProgress.mode & ProfileMode) === NoContext934 ) {935 next = completeWork(current, workInProgress, renderExpirationTime);936 } else {937 startProfilerTimer(workInProgress);938 next = completeWork(current, workInProgress, renderExpirationTime);939 // Update render duration assuming we didn't error.940 stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);941 }942 stopWorkTimer(workInProgress);943 resetCurrentDebugFiberInDEV();944 resetChildExpirationTime(workInProgress);945 if (next !== null) {946 // Completing this fiber spawned new work. Work on that next.947 return next;948 }949 if (950 returnFiber !== null &&951 // Do not append effects to parents if a sibling failed to complete952 (returnFiber.effectTag & Incomplete) === NoEffect953 ) {954 // Append all the effects of the subtree and this fiber onto the effect955 // list of the parent. The completion order of the children affects the956 // side-effect order.957 if (returnFiber.firstEffect === null) {958 returnFiber.firstEffect = workInProgress.firstEffect;959 }960 if (workInProgress.lastEffect !== null) {961 if (returnFiber.lastEffect !== null) {962 returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;963 }964 returnFiber.lastEffect = workInProgress.lastEffect;965 }966 // If this fiber had side-effects, we append it AFTER the children's967 // side-effects. We can perform certain side-effects earlier if needed,968 // by doing multiple passes over the effect list. We don't want to969 // schedule our own side-effect on our own list because if end up970 // reusing children we'll schedule this effect onto itself since we're971 // at the end.972 const effectTag = workInProgress.effectTag;973 // Skip both NoWork and PerformedWork tags when creating the effect974 // list. PerformedWork effect is read by React DevTools but shouldn't be975 // committed.976 if (effectTag > PerformedWork) {977 if (returnFiber.lastEffect !== null) {978 returnFiber.lastEffect.nextEffect = workInProgress;979 } else {980 returnFiber.firstEffect = workInProgress;981 }982 returnFiber.lastEffect = workInProgress;983 }984 }985 } else {986 // This fiber did not complete because something threw. Pop values off987 // the stack without entering the complete phase. If this is a boundary,988 // capture values if possible.989 const next = unwindWork(workInProgress, renderExpirationTime);990 // Because this fiber did not complete, don't reset its expiration time.991 if (992 enableProfilerTimer &&993 (workInProgress.mode & ProfileMode) !== NoContext994 ) {995 // Record the render duration for the fiber that errored.996 stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);997 // Include the time spent working on failed children before continuing.998 let actualDuration = workInProgress.actualDuration;999 let child = workInProgress.child;1000 while (child !== null) {1001 actualDuration += child.actualDuration;1002 child = child.sibling;1003 }1004 workInProgress.actualDuration = actualDuration;1005 }1006 if (next !== null) {1007 // If completing this work spawned new work, do that next. We'll come1008 // back here again.1009 // Since we're restarting, remove anything that is not a host effect1010 // from the effect tag.1011 // TODO: The name stopFailedWorkTimer is misleading because Suspense1012 // also captures and restarts.1013 stopFailedWorkTimer(workInProgress);1014 next.effectTag &= HostEffectMask;1015 return next;1016 }1017 stopWorkTimer(workInProgress);1018 if (returnFiber !== null) {1019 // Mark the parent fiber as incomplete and clear its effect list.1020 returnFiber.firstEffect = returnFiber.lastEffect = null;1021 returnFiber.effectTag |= Incomplete;1022 }1023 }1024 const siblingFiber = workInProgress.sibling;1025 if (siblingFiber !== null) {1026 // If there is more work to do in this returnFiber, do that next.1027 return siblingFiber;1028 }1029 // Otherwise, return to the parent1030 workInProgress = returnFiber;1031 } while (workInProgress !== null);1032 // We've reached the root.1033 if (workInProgressRootExitStatus === RootIncomplete) {1034 workInProgressRootExitStatus = RootCompleted;1035 }1036 return null;1037}1038function resetChildExpirationTime(completedWork: Fiber) {1039 if (1040 renderExpirationTime !== Never &&1041 completedWork.childExpirationTime === Never1042 ) {1043 // The children of this component are hidden. Don't bubble their1044 // expiration times.1045 return;1046 }1047 let newChildExpirationTime = NoWork;1048 // Bubble up the earliest expiration time.1049 if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoContext) {1050 // In profiling mode, resetChildExpirationTime is also used to reset1051 // profiler durations.1052 let actualDuration = completedWork.actualDuration;1053 let treeBaseDuration = completedWork.selfBaseDuration;1054 // When a fiber is cloned, its actualDuration is reset to 0. This value will1055 // only be updated if work is done on the fiber (i.e. it doesn't bailout).1056 // When work is done, it should bubble to the parent's actualDuration. If1057 // the fiber has not been cloned though, (meaning no work was done), then1058 // this value will reflect the amount of time spent working on a previous1059 // render. In that case it should not bubble. We determine whether it was1060 // cloned by comparing the child pointer.1061 const shouldBubbleActualDurations =1062 completedWork.alternate === null ||1063 completedWork.child !== completedWork.alternate.child;1064 let child = completedWork.child;1065 while (child !== null) {1066 const childUpdateExpirationTime = child.expirationTime;1067 const childChildExpirationTime = child.childExpirationTime;1068 if (childUpdateExpirationTime > newChildExpirationTime) {1069 newChildExpirationTime = childUpdateExpirationTime;1070 }1071 if (childChildExpirationTime > newChildExpirationTime) {1072 newChildExpirationTime = childChildExpirationTime;1073 }1074 if (shouldBubbleActualDurations) {1075 actualDuration += child.actualDuration;1076 }1077 treeBaseDuration += child.treeBaseDuration;1078 child = child.sibling;1079 }1080 completedWork.actualDuration = actualDuration;1081 completedWork.treeBaseDuration = treeBaseDuration;1082 } else {1083 let child = completedWork.child;1084 while (child !== null) {1085 const childUpdateExpirationTime = child.expirationTime;1086 const childChildExpirationTime = child.childExpirationTime;1087 if (childUpdateExpirationTime > newChildExpirationTime) {1088 newChildExpirationTime = childUpdateExpirationTime;1089 }1090 if (childChildExpirationTime > newChildExpirationTime) {1091 newChildExpirationTime = childChildExpirationTime;1092 }1093 child = child.sibling;1094 }1095 }1096 completedWork.childExpirationTime = newChildExpirationTime;1097}1098function commitRoot(root, expirationTime) {1099 runWithPriority(1100 ImmediatePriority,1101 commitRootImpl.bind(null, root, expirationTime),1102 );1103 // If there are passive effects, schedule a callback to flush them. This goes1104 // outside commitRootImpl so that it inherits the priority of the render.1105 if (rootWithPendingPassiveEffects !== null) {1106 const priorityLevel = getCurrentPriorityLevel();1107 scheduleCallback(priorityLevel, () => {1108 flushPassiveEffects();1109 return null;1110 });1111 }1112 return null;1113}1114function commitRootImpl(root, expirationTime) {1115 flushPassiveEffects();1116 flushRenderPhaseStrictModeWarningsInDEV();1117 invariant(1118 workPhase !== RenderPhase && workPhase !== CommitPhase,1119 'Should not already be working.',1120 );1121 const finishedWork = root.current.alternate;1122 invariant(finishedWork !== null, 'Should have a work-in-progress root.');1123 // commitRoot never returns a continuation; it always finishes synchronously.1124 // So we can clear these now to allow a new callback to be scheduled.1125 root.callbackNode = null;1126 root.callbackExpirationTime = NoWork;1127 startCommitTimer();1128 // Update the first and last pending times on this root. The new first1129 // pending time is whatever is left on the root fiber.1130 const updateExpirationTimeBeforeCommit = finishedWork.expirationTime;1131 const childExpirationTimeBeforeCommit = finishedWork.childExpirationTime;1132 const firstPendingTimeBeforeCommit =1133 childExpirationTimeBeforeCommit > updateExpirationTimeBeforeCommit1134 ? childExpirationTimeBeforeCommit1135 : updateExpirationTimeBeforeCommit;1136 root.firstPendingTime = firstPendingTimeBeforeCommit;1137 if (firstPendingTimeBeforeCommit < root.lastPendingTime) {1138 // This usually means we've finished all the work, but it can also happen1139 // when something gets downprioritized during render, like a hidden tree.1140 root.lastPendingTime = firstPendingTimeBeforeCommit;1141 }1142 if (root === workInProgressRoot) {1143 // We can reset these now that they are finished.1144 workInProgressRoot = null;1145 workInProgress = null;1146 renderExpirationTime = NoWork;1147 } else {1148 // This indicates that the last root we worked on is not the same one that1149 // we're committing now. This most commonly happens when a suspended root1150 // times out.1151 }1152 // Get the list of effects.1153 let firstEffect;1154 if (finishedWork.effectTag > PerformedWork) {1155 // A fiber's effect list consists only of its children, not itself. So if1156 // the root has an effect, we need to add it to the end of the list. The1157 // resulting list is the set that would belong to the root's parent, if it1158 // had one; that is, all the effects in the tree including the root.1159 if (finishedWork.lastEffect !== null) {1160 finishedWork.lastEffect.nextEffect = finishedWork;1161 firstEffect = finishedWork.firstEffect;1162 } else {1163 firstEffect = finishedWork;1164 }1165 } else {1166 // There is no effect on the root.1167 firstEffect = finishedWork.firstEffect;1168 }1169 if (firstEffect !== null) {1170 const prevWorkPhase = workPhase;1171 workPhase = CommitPhase;1172 let prevInteractions: Set<Interaction> | null = null;1173 if (enableSchedulerTracing) {1174 prevInteractions = __interactionsRef.current;1175 __interactionsRef.current = root.memoizedInteractions;1176 }1177 // Reset this to null before calling lifecycles1178 ReactCurrentOwner.current = null;1179 // The commit phase is broken into several sub-phases. We do a separate pass1180 // of the effect list for each phase: all mutation effects come before all1181 // layout effects, and so on.1182 // The first phase a "before mutation" phase. We use this phase to read the1183 // state of the host tree right before we mutate it. This is where1184 // getSnapshotBeforeUpdate is called.1185 startCommitSnapshotEffectsTimer();1186 prepareForCommit(root.containerInfo);1187 nextEffect = firstEffect;1188 do {1189 if (__DEV__) {1190 invokeGuardedCallback(null, commitBeforeMutationEffects, null);1191 if (hasCaughtError()) {1192 invariant(nextEffect !== null, 'Should be working on an effect.');1193 const error = clearCaughtError();1194 captureCommitPhaseError(nextEffect, error);1195 nextEffect = nextEffect.nextEffect;1196 }1197 } else {1198 try {1199 commitBeforeMutationEffects();1200 } catch (error) {1201 invariant(nextEffect !== null, 'Should be working on an effect.');1202 captureCommitPhaseError(nextEffect, error);1203 nextEffect = nextEffect.nextEffect;1204 }1205 }1206 } while (nextEffect !== null);1207 stopCommitSnapshotEffectsTimer();1208 if (enableProfilerTimer) {1209 // Mark the current commit time to be shared by all Profilers in this1210 // batch. This enables them to be grouped later.1211 recordCommitTime();1212 }1213 // The next phase is the mutation phase, where we mutate the host tree.1214 startCommitHostEffectsTimer();1215 nextEffect = firstEffect;1216 do {1217 if (__DEV__) {1218 invokeGuardedCallback(null, commitMutationEffects, null);1219 if (hasCaughtError()) {1220 invariant(nextEffect !== null, 'Should be working on an effect.');1221 const error = clearCaughtError();1222 captureCommitPhaseError(nextEffect, error);1223 nextEffect = nextEffect.nextEffect;1224 }1225 } else {1226 try {1227 commitMutationEffects();1228 } catch (error) {1229 invariant(nextEffect !== null, 'Should be working on an effect.');1230 captureCommitPhaseError(nextEffect, error);1231 nextEffect = nextEffect.nextEffect;1232 }1233 }1234 } while (nextEffect !== null);1235 stopCommitHostEffectsTimer();1236 resetAfterCommit(root.containerInfo);1237 // The work-in-progress tree is now the current tree. This must come after1238 // the mutation phase, so that the previous tree is still current during1239 // componentWillUnmount, but before the layout phase, so that the finished1240 // work is current during componentDidMount/Update.1241 root.current = finishedWork;1242 // The next phase is the layout phase, where we call effects that read1243 // the host tree after it's been mutated. The idiomatic use case for this is1244 // layout, but class component lifecycles also fire here for legacy reasons.1245 startCommitLifeCyclesTimer();1246 nextEffect = firstEffect;1247 do {1248 if (__DEV__) {1249 invokeGuardedCallback(1250 null,1251 commitLayoutEffects,1252 null,1253 root,1254 expirationTime,1255 );1256 if (hasCaughtError()) {1257 invariant(nextEffect !== null, 'Should be working on an effect.');1258 const error = clearCaughtError();1259 captureCommitPhaseError(nextEffect, error);1260 nextEffect = nextEffect.nextEffect;1261 }1262 } else {1263 try {1264 commitLayoutEffects(root, expirationTime);1265 } catch (error) {1266 invariant(nextEffect !== null, 'Should be working on an effect.');1267 captureCommitPhaseError(nextEffect, error);1268 nextEffect = nextEffect.nextEffect;1269 }1270 }1271 } while (nextEffect !== null);1272 stopCommitLifeCyclesTimer();1273 nextEffect = null;1274 if (enableSchedulerTracing) {1275 __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1276 }1277 workPhase = prevWorkPhase;1278 } else {1279 // No effects.1280 root.current = finishedWork;1281 // Measure these anyway so the flamegraph explicitly shows that there were1282 // no effects.1283 // TODO: Maybe there's a better way to report this.1284 startCommitSnapshotEffectsTimer();1285 stopCommitSnapshotEffectsTimer();1286 if (enableProfilerTimer) {1287 recordCommitTime();1288 }1289 startCommitHostEffectsTimer();1290 stopCommitHostEffectsTimer();1291 startCommitLifeCyclesTimer();1292 stopCommitLifeCyclesTimer();1293 }1294 stopCommitTimer();1295 if (rootDoesHavePassiveEffects) {1296 // This commit has passive effects. Stash a reference to them. But don't1297 // schedule a callback until after flushing layout work.1298 rootDoesHavePassiveEffects = false;1299 rootWithPendingPassiveEffects = root;1300 pendingPassiveEffectsExpirationTime = expirationTime;1301 } else {1302 if (enableSchedulerTracing) {1303 // If there are no passive effects, then we can complete the pending1304 // interactions. Otherwise, we'll wait until after the passive effects1305 // are flushed.1306 finishPendingInteractions(root, expirationTime);1307 }1308 }1309 // Check if there's remaining work on this root1310 const remainingExpirationTime = root.firstPendingTime;1311 if (remainingExpirationTime !== NoWork) {1312 const currentTime = requestCurrentTime();1313 const priorityLevel = inferPriorityFromExpirationTime(1314 currentTime,1315 remainingExpirationTime,1316 );1317 scheduleCallbackForRoot(root, priorityLevel, remainingExpirationTime);1318 } else {1319 // If there's no remaining work, we can clear the set of already failed1320 // error boundaries.1321 legacyErrorBoundariesThatAlreadyFailed = null;1322 }1323 onCommitRoot(finishedWork.stateNode);1324 if (remainingExpirationTime === Sync) {1325 // Count the number of times the root synchronously re-renders without1326 // finishing. If there are too many, it indicates an infinite update loop.1327 if (root === rootWithNestedUpdates) {1328 nestedUpdateCount++;1329 } else {1330 nestedUpdateCount = 0;1331 rootWithNestedUpdates = root;1332 }1333 } else {1334 nestedUpdateCount = 0;1335 }1336 if (hasUncaughtError) {1337 hasUncaughtError = false;1338 const error = firstUncaughtError;1339 firstUncaughtError = null;1340 throw error;1341 }1342 if (workPhase === LegacyUnbatchedPhase) {1343 // This is a legacy edge case. We just committed the initial mount of1344 // a ReactDOM.render-ed root inside of batchedUpdates. The commit fired1345 // synchronously, but layout updates should be deferred until the end1346 // of the batch.1347 return null;1348 }1349 // If layout work was scheduled, flush it now.1350 flushImmediateQueue();1351 return null;1352}1353function commitBeforeMutationEffects() {1354 while (nextEffect !== null) {1355 if ((nextEffect.effectTag & Snapshot) !== NoEffect) {1356 setCurrentDebugFiberInDEV(nextEffect);1357 recordEffect();1358 const current = nextEffect.alternate;1359 commitBeforeMutationEffectOnFiber(current, nextEffect);1360 resetCurrentDebugFiberInDEV();1361 }1362 nextEffect = nextEffect.nextEffect;1363 }1364}1365function commitMutationEffects() {1366 // TODO: Should probably move the bulk of this function to commitWork.1367 while (nextEffect !== null) {1368 setCurrentDebugFiberInDEV(nextEffect);1369 const effectTag = nextEffect.effectTag;1370 if (effectTag & ContentReset) {1371 commitResetTextContent(nextEffect);1372 }1373 if (effectTag & Ref) {1374 const current = nextEffect.alternate;1375 if (current !== null) {1376 commitDetachRef(current);1377 }1378 }1379 // The following switch statement is only concerned about placement,1380 // updates, and deletions. To avoid needing to add a case for every possible1381 // bitmap value, we remove the secondary effects from the effect tag and1382 // switch on that value.1383 let primaryEffectTag = effectTag & (Placement | Update | Deletion);1384 switch (primaryEffectTag) {1385 case Placement: {1386 commitPlacement(nextEffect);1387 // Clear the "placement" from effect tag so that we know that this is1388 // inserted, before any life-cycles like componentDidMount gets called.1389 // TODO: findDOMNode doesn't rely on this any more but isMounted does1390 // and isMounted is deprecated anyway so we should be able to kill this.1391 nextEffect.effectTag &= ~Placement;1392 break;1393 }1394 case PlacementAndUpdate: {1395 // Placement1396 commitPlacement(nextEffect);1397 // Clear the "placement" from effect tag so that we know that this is1398 // inserted, before any life-cycles like componentDidMount gets called.1399 nextEffect.effectTag &= ~Placement;1400 // Update1401 const current = nextEffect.alternate;1402 commitWork(current, nextEffect);1403 break;1404 }1405 case Update: {1406 const current = nextEffect.alternate;1407 commitWork(current, nextEffect);1408 break;1409 }1410 case Deletion: {1411 commitDeletion(nextEffect);1412 break;1413 }1414 }1415 // TODO: Only record a mutation effect if primaryEffectTag is non-zero.1416 recordEffect();1417 resetCurrentDebugFiberInDEV();1418 nextEffect = nextEffect.nextEffect;1419 }1420}1421function commitLayoutEffects(1422 root: FiberRoot,1423 committedExpirationTime: ExpirationTime,1424) {1425 // TODO: Should probably move the bulk of this function to commitWork.1426 while (nextEffect !== null) {1427 setCurrentDebugFiberInDEV(nextEffect);1428 const effectTag = nextEffect.effectTag;1429 if (effectTag & (Update | Callback)) {1430 recordEffect();1431 const current = nextEffect.alternate;1432 commitLayoutEffectOnFiber(1433 root,1434 current,1435 nextEffect,1436 committedExpirationTime,1437 );1438 }1439 if (effectTag & Ref) {1440 recordEffect();1441 commitAttachRef(nextEffect);1442 }1443 if (effectTag & Passive) {1444 rootDoesHavePassiveEffects = true;1445 }1446 resetCurrentDebugFiberInDEV();1447 nextEffect = nextEffect.nextEffect;1448 }1449}1450export function flushPassiveEffects() {1451 if (rootWithPendingPassiveEffects === null) {1452 return false;1453 }1454 const root = rootWithPendingPassiveEffects;1455 const expirationTime = pendingPassiveEffectsExpirationTime;1456 rootWithPendingPassiveEffects = null;1457 pendingPassiveEffectsExpirationTime = NoWork;1458 let prevInteractions: Set<Interaction> | null = null;1459 if (enableSchedulerTracing) {1460 prevInteractions = __interactionsRef.current;1461 __interactionsRef.current = root.memoizedInteractions;1462 }1463 invariant(1464 workPhase !== RenderPhase && workPhase !== CommitPhase,1465 'Cannot flush passive effects while already rendering.',1466 );1467 const prevWorkPhase = workPhase;1468 workPhase = CommitPhase;1469 // Note: This currently assumes there are no passive effects on the root1470 // fiber, because the root is not part of its own effect list. This could1471 // change in the future.1472 let effect = root.current.firstEffect;1473 while (effect !== null) {1474 if (__DEV__) {1475 setCurrentDebugFiberInDEV(effect);1476 invokeGuardedCallback(null, commitPassiveHookEffects, null, effect);1477 if (hasCaughtError()) {1478 invariant(effect !== null, 'Should be working on an effect.');1479 const error = clearCaughtError();1480 captureCommitPhaseError(effect, error);1481 }1482 resetCurrentDebugFiberInDEV();1483 } else {1484 try {1485 commitPassiveHookEffects(effect);1486 } catch (error) {1487 invariant(effect !== null, 'Should be working on an effect.');1488 captureCommitPhaseError(effect, error);1489 }1490 }1491 effect = effect.nextEffect;1492 }1493 if (enableSchedulerTracing) {1494 __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1495 finishPendingInteractions(root, expirationTime);1496 }1497 workPhase = prevWorkPhase;1498 flushImmediateQueue();1499 // If additional passive effects were scheduled, increment a counter. If this1500 // exceeds the limit, we'll fire a warning.1501 nestedPassiveUpdateCount =1502 rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;1503 return true;1504}1505export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {1506 return (1507 legacyErrorBoundariesThatAlreadyFailed !== null &&1508 legacyErrorBoundariesThatAlreadyFailed.has(instance)1509 );1510}1511export function markLegacyErrorBoundaryAsFailed(instance: mixed) {1512 if (legacyErrorBoundariesThatAlreadyFailed === null) {1513 legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);1514 } else {1515 legacyErrorBoundariesThatAlreadyFailed.add(instance);1516 }1517}1518function prepareToThrowUncaughtError(error: mixed) {1519 if (!hasUncaughtError) {1520 hasUncaughtError = true;1521 firstUncaughtError = error;1522 }1523}1524export const onUncaughtError = prepareToThrowUncaughtError;1525function captureCommitPhaseErrorOnRoot(1526 rootFiber: Fiber,1527 sourceFiber: Fiber,1528 error: mixed,1529) {1530 const errorInfo = createCapturedValue(error, sourceFiber);1531 const update = createRootErrorUpdate(rootFiber, errorInfo, Sync);1532 enqueueUpdate(rootFiber, update);1533 const root = markUpdateTimeFromFiberToRoot(rootFiber, Sync);1534 if (root !== null) {1535 scheduleCallbackForRoot(root, ImmediatePriority, Sync);1536 }1537}1538export function captureCommitPhaseError(sourceFiber: Fiber, error: mixed) {1539 if (sourceFiber.tag === HostRoot) {1540 // Error was thrown at the root. There is no parent, so the root1541 // itself should capture it.1542 captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);1543 return;1544 }1545 let fiber = sourceFiber.return;1546 while (fiber !== null) {1547 if (fiber.tag === HostRoot) {1548 captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);1549 return;1550 } else if (fiber.tag === ClassComponent) {1551 const ctor = fiber.type;1552 const instance = fiber.stateNode;1553 if (1554 typeof ctor.getDerivedStateFromError === 'function' ||1555 (typeof instance.componentDidCatch === 'function' &&1556 !isAlreadyFailedLegacyErrorBoundary(instance))1557 ) {1558 const errorInfo = createCapturedValue(error, sourceFiber);1559 const update = createClassErrorUpdate(1560 fiber,1561 errorInfo,1562 // TODO: This is always sync1563 Sync,1564 );1565 enqueueUpdate(fiber, update);1566 const root = markUpdateTimeFromFiberToRoot(fiber, Sync);1567 if (root !== null) {1568 scheduleCallbackForRoot(root, ImmediatePriority, Sync);1569 }1570 return;1571 }1572 }1573 fiber = fiber.return;1574 }1575}1576export function pingSuspendedRoot(1577 root: FiberRoot,1578 thenable: Thenable,1579 suspendedTime: ExpirationTime,1580) {1581 const pingCache = root.pingCache;1582 if (pingCache !== null) {1583 // The thenable resolved, so we no longer need to memoize, because it will1584 // never be thrown again.1585 pingCache.delete(thenable);1586 }1587 if (workInProgressRoot === root && renderExpirationTime === suspendedTime) {1588 // Received a ping at the same priority level at which we're currently1589 // rendering. Restart from the root. Don't need to schedule a ping because1590 // we're already working on this tree.1591 prepareFreshStack(root, renderExpirationTime);1592 return;1593 }1594 const lastPendingTime = root.lastPendingTime;1595 if (lastPendingTime < suspendedTime) {1596 // The root is no longer suspended at this time.1597 return;1598 }1599 const pingTime = root.pingTime;1600 if (pingTime !== NoWork && pingTime < suspendedTime) {1601 // There's already a lower priority ping scheduled.1602 return;1603 }1604 // Mark the time at which this ping was scheduled.1605 root.pingTime = suspendedTime;1606 const currentTime = requestCurrentTime();1607 const priorityLevel = inferPriorityFromExpirationTime(1608 currentTime,1609 suspendedTime,1610 );1611 scheduleCallbackForRoot(root, priorityLevel, suspendedTime);1612}1613export function retryTimedOutBoundary(boundaryFiber: Fiber) {1614 // The boundary fiber (a Suspense component) previously timed out and was1615 // rendered in its fallback state. One of the promises that suspended it has1616 // resolved, which means at least part of the tree was likely unblocked. Try1617 // rendering again, at a new expiration time.1618 const currentTime = requestCurrentTime();1619 const retryTime = computeExpirationForFiber(currentTime, boundaryFiber);1620 // TODO: Special case idle priority?1621 const priorityLevel = inferPriorityFromExpirationTime(currentTime, retryTime);1622 const root = markUpdateTimeFromFiberToRoot(boundaryFiber, retryTime);1623 if (root !== null) {1624 scheduleCallbackForRoot(root, priorityLevel, retryTime);1625 }1626}1627export function resolveRetryThenable(boundaryFiber: Fiber, thenable: Thenable) {1628 let retryCache: WeakSet<Thenable> | Set<Thenable> | null;1629 if (enableSuspenseServerRenderer) {1630 switch (boundaryFiber.tag) {1631 case SuspenseComponent:1632 retryCache = boundaryFiber.stateNode;1633 break;1634 case DehydratedSuspenseComponent:1635 retryCache = boundaryFiber.memoizedState;1636 break;1637 default:1638 invariant(1639 false,1640 'Pinged unknown suspense boundary type. ' +1641 'This is probably a bug in React.',1642 );1643 }1644 } else {1645 retryCache = boundaryFiber.stateNode;1646 }1647 if (retryCache !== null) {1648 // The thenable resolved, so we no longer need to memoize, because it will1649 // never be thrown again.1650 retryCache.delete(thenable);1651 }1652 retryTimedOutBoundary(boundaryFiber);1653}1654// Computes the next Just Noticeable Difference (JND) boundary.1655// The theory is that a person can't tell the difference between small differences in time.1656// Therefore, if we wait a bit longer than necessary that won't translate to a noticeable1657// difference in the experience. However, waiting for longer might mean that we can avoid1658// showing an intermediate loading state. The longer we have already waited, the harder it1659// is to tell small differences in time. Therefore, the longer we've already waited,1660// the longer we can wait additionally. At some point we have to give up though.1661// We pick a train model where the next boundary commits at a consistent schedule.1662// These particular numbers are vague estimates. We expect to adjust them based on research.1663function jnd(timeElapsed: number) {1664 return timeElapsed < 1201665 ? 1201666 : timeElapsed < 4801667 ? 4801668 : timeElapsed < 10801669 ? 10801670 : timeElapsed < 19201671 ? 19201672 : timeElapsed < 30001673 ? 30001674 : timeElapsed < 43201675 ? 43201676 : ceil(timeElapsed / 1960) * 1960;1677}1678function computeMsUntilTimeout(1679 mostRecentEventTime: ExpirationTime,1680 committedExpirationTime: ExpirationTime,1681) {1682 if (disableYielding) {1683 // Timeout immediately when yielding is disabled.1684 return 0;1685 }1686 const eventTimeMs: number = inferTimeFromExpirationTime(mostRecentEventTime);1687 const currentTimeMs: number = now();1688 const timeElapsed = currentTimeMs - eventTimeMs;1689 let msUntilTimeout = jnd(timeElapsed) - timeElapsed;1690 // Compute the time until this render pass would expire.1691 const timeUntilExpirationMs =1692 expirationTimeToMs(committedExpirationTime) - currentTimeMs;1693 // Clamp the timeout to the expiration time.1694 // TODO: Once the event time is exact instead of inferred from expiration time1695 // we don't need this.1696 if (timeUntilExpirationMs < msUntilTimeout) {1697 msUntilTimeout = timeUntilExpirationMs;1698 }1699 // This is the value that is passed to `setTimeout`.1700 return msUntilTimeout;1701}1702function checkForNestedUpdates() {1703 if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {1704 nestedUpdateCount = 0;1705 rootWithNestedUpdates = null;1706 invariant(1707 false,1708 'Maximum update depth exceeded. This can happen when a component ' +1709 'repeatedly calls setState inside componentWillUpdate or ' +1710 'componentDidUpdate. React limits the number of nested updates to ' +1711 'prevent infinite loops.',1712 );1713 }1714 if (__DEV__) {1715 if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {1716 nestedPassiveUpdateCount = 0;1717 warning(1718 false,1719 'Maximum update depth exceeded. This can happen when a component ' +1720 "calls setState inside useEffect, but useEffect either doesn't " +1721 'have a dependency array, or one of the dependencies changes on ' +1722 'every render.',1723 );1724 }1725 }1726}1727function flushRenderPhaseStrictModeWarningsInDEV() {1728 if (__DEV__) {1729 ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();1730 ReactStrictModeWarnings.flushLegacyContextWarning();1731 if (warnAboutDeprecatedLifecycles) {1732 ReactStrictModeWarnings.flushPendingDeprecationWarnings();1733 }1734 }1735}1736function stopFinishedWorkLoopTimer() {1737 const didCompleteRoot = true;1738 stopWorkLoopTimer(interruptedBy, didCompleteRoot);1739 interruptedBy = null;1740}1741function stopInterruptedWorkLoopTimer() {1742 // TODO: Track which fiber caused the interruption.1743 const didCompleteRoot = false;1744 stopWorkLoopTimer(interruptedBy, didCompleteRoot);1745 interruptedBy = null;1746}1747function checkForInterruption(1748 fiberThatReceivedUpdate: Fiber,1749 updateExpirationTime: ExpirationTime,1750) {1751 if (1752 enableUserTimingAPI &&1753 workInProgressRoot !== null &&1754 updateExpirationTime > renderExpirationTime1755 ) {1756 interruptedBy = fiberThatReceivedUpdate;1757 }1758}1759let didWarnStateUpdateForUnmountedComponent: Set<string> | null = null;1760function warnAboutUpdateOnUnmountedFiberInDEV(fiber) {1761 if (__DEV__) {1762 const tag = fiber.tag;1763 if (1764 tag !== HostRoot &&1765 tag !== ClassComponent &&1766 tag !== FunctionComponent &&1767 tag !== ForwardRef &&1768 tag !== MemoComponent &&1769 tag !== SimpleMemoComponent1770 ) {1771 // Only warn for user-defined components, not internal ones like Suspense.1772 return;1773 }1774 // We show the whole stack but dedupe on the top component's name because...

Full Screen

Full Screen

ReactFiberScheduler.new.js

Source:ReactFiberScheduler.new.js Github

copy

Full Screen

...283 checkForNestedUpdates();284 warnAboutInvalidUpdatesOnClassComponentsInDEV(fiber);285 const root = markUpdateTimeFromFiberToRoot(fiber, expirationTime);286 if (root === null) {287 warnAboutUpdateOnUnmountedFiberInDEV(fiber);288 return;289 }290 root.pingTime = NoWork;291 checkForInterruption(fiber, expirationTime);292 recordScheduleUpdate();293 if (expirationTime === Sync) {294 if (workPhase === LegacyUnbatchedPhase) {295 // This is a legacy edge case. The initial mount of a ReactDOM.render-ed296 // root inside of batchedUpdates should be synchronous, but layout updates297 // should be deferred until the end of the batch.298 let callback = renderRoot(root, Sync, true);299 while (callback !== null) {300 callback = callback(true);301 }302 } else {303 scheduleCallbackForRoot(root, ImmediatePriority, Sync);304 if (workPhase === NotWorking) {305 // Flush the synchronous work now, wnless we're already working or inside306 // a batch. This is intentionally inside scheduleUpdateOnFiber instead of307 // scheduleCallbackForFiber to preserve the ability to schedule a callback308 // without immediately flushing it. We only do this for user-initated309 // updates, to preserve historical behavior of sync mode.310 flushImmediateQueue();311 }312 }313 } else {314 // TODO: computeExpirationForFiber also reads the priority. Pass the315 // priority as an argument to that function and this one.316 const priorityLevel = getCurrentPriorityLevel();317 if (priorityLevel === UserBlockingPriority) {318 // This is the result of a discrete event. Track the lowest priority319 // discrete update per root so we can flush them early, if needed.320 if (rootsWithPendingDiscreteUpdates === null) {321 rootsWithPendingDiscreteUpdates = new Map([[root, expirationTime]]);322 } else {323 const lastDiscreteTime = rootsWithPendingDiscreteUpdates.get(root);324 if (325 lastDiscreteTime === undefined ||326 lastDiscreteTime > expirationTime327 ) {328 rootsWithPendingDiscreteUpdates.set(root, expirationTime);329 }330 }331 }332 scheduleCallbackForRoot(root, priorityLevel, expirationTime);333 }334}335export const scheduleWork = scheduleUpdateOnFiber;336// This is split into a separate function so we can mark a fiber with pending337// work without treating it as a typical update that originates from an event;338// e.g. retrying a Suspense boundary isn't an update, but it does schedule work339// on a fiber.340function markUpdateTimeFromFiberToRoot(fiber, expirationTime) {341 // Update the source fiber's expiration time342 if (fiber.expirationTime < expirationTime) {343 fiber.expirationTime = expirationTime;344 }345 let alternate = fiber.alternate;346 if (alternate !== null && alternate.expirationTime < expirationTime) {347 alternate.expirationTime = expirationTime;348 }349 // Walk the parent path to the root and update the child expiration time.350 let node = fiber.return;351 let root = null;352 if (node === null && fiber.tag === HostRoot) {353 root = fiber.stateNode;354 } else {355 while (node !== null) {356 alternate = node.alternate;357 if (node.childExpirationTime < expirationTime) {358 node.childExpirationTime = expirationTime;359 if (360 alternate !== null &&361 alternate.childExpirationTime < expirationTime362 ) {363 alternate.childExpirationTime = expirationTime;364 }365 } else if (366 alternate !== null &&367 alternate.childExpirationTime < expirationTime368 ) {369 alternate.childExpirationTime = expirationTime;370 }371 if (node.return === null && node.tag === HostRoot) {372 root = node.stateNode;373 break;374 }375 node = node.return;376 }377 }378 if (root !== null) {379 // Update the first and last pending expiration times in this root380 const firstPendingTime = root.firstPendingTime;381 if (expirationTime > firstPendingTime) {382 root.firstPendingTime = expirationTime;383 }384 const lastPendingTime = root.lastPendingTime;385 if (lastPendingTime === NoWork || expirationTime < lastPendingTime) {386 root.lastPendingTime = expirationTime;387 }388 }389 return root;390}391// Use this function, along with runRootCallback, to ensure that only a single392// callback per root is scheduled. It's still possible to call renderRoot393// directly, but scheduling via this function helps avoid excessive callbacks.394// It works by storing the callback node and expiration time on the root. When a395// new callback comes in, it compares the expiration time to determine if it396// should cancel the previous one. It also relies on commitRoot scheduling a397// callback to render the next level, because that means we don't need a398// separate callback per expiration time.399function scheduleCallbackForRoot(400 root: FiberRoot,401 priorityLevel: ReactPriorityLevel,402 expirationTime: ExpirationTime,403) {404 const existingCallbackExpirationTime = root.callbackExpirationTime;405 if (existingCallbackExpirationTime < expirationTime) {406 // New callback has higher priority than the existing one.407 const existingCallbackNode = root.callbackNode;408 if (existingCallbackNode !== null) {409 cancelCallback(existingCallbackNode);410 }411 root.callbackExpirationTime = expirationTime;412 const options =413 expirationTime === Sync414 ? null415 : {timeout: expirationTimeToMs(expirationTime)};416 root.callbackNode = scheduleCallback(417 priorityLevel,418 runRootCallback.bind(419 null,420 root,421 renderRoot.bind(null, root, expirationTime),422 ),423 options,424 );425 if (426 enableUserTimingAPI &&427 expirationTime !== Sync &&428 workPhase !== RenderPhase &&429 workPhase !== CommitPhase430 ) {431 // Scheduled an async callback, and we're not already working. Add an432 // entry to the flamegraph that shows we're waiting for a callback433 // to fire.434 startRequestCallbackTimer();435 }436 }437 const timeoutHandle = root.timeoutHandle;438 if (timeoutHandle !== noTimeout) {439 // The root previous suspended and scheduled a timeout to commit a fallback440 // state. Now that we have additional work, cancel the timeout.441 root.timeoutHandle = noTimeout;442 // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above443 cancelTimeout(timeoutHandle);444 }445 // Add the current set of interactions to the pending set associated with446 // this root.447 schedulePendingInteraction(root, expirationTime);448}449function runRootCallback(root, callback, isSync) {450 const prevCallbackNode = root.callbackNode;451 let continuation = null;452 try {453 continuation = callback(isSync);454 if (continuation !== null) {455 return runRootCallback.bind(null, root, continuation);456 } else {457 return null;458 }459 } finally {460 // If the callback exits without returning a continuation, remove the461 // corresponding callback node from the root. Unless the callback node462 // has changed, which implies that it was already cancelled by a high463 // priority update.464 if (continuation === null && prevCallbackNode === root.callbackNode) {465 root.callbackNode = null;466 root.callbackExpirationTime = NoWork;467 }468 }469}470export function flushRoot(root: FiberRoot, expirationTime: ExpirationTime) {471 if (workPhase === RenderPhase || workPhase === CommitPhase) {472 invariant(473 false,474 'work.commit(): Cannot commit while already rendering. This likely ' +475 'means you attempted to commit from inside a lifecycle method.',476 );477 }478 scheduleCallback(479 ImmediatePriority,480 renderRoot.bind(null, root, expirationTime),481 );482 flushImmediateQueue();483}484export function flushInteractiveUpdates() {485 if (workPhase === RenderPhase || workPhase === CommitPhase) {486 // Can't synchronously flush interactive updates if React is already487 // working. This is currently a no-op.488 // TODO: Should we fire a warning? This happens if you synchronously invoke489 // an input event inside an effect, like with `element.click()`.490 return;491 }492 flushPendingDiscreteUpdates();493}494function resolveLocksOnRoot(root: FiberRoot, expirationTime: ExpirationTime) {495 const firstBatch = root.firstBatch;496 if (497 firstBatch !== null &&498 firstBatch._defer &&499 firstBatch._expirationTime >= expirationTime500 ) {501 root.finishedWork = root.current.alternate;502 root.pendingCommitExpirationTime = expirationTime;503 scheduleCallback(NormalPriority, () => {504 firstBatch._onComplete();505 return null;506 });507 return true;508 } else {509 return false;510 }511}512export function deferredUpdates<A>(fn: () => A): A {513 // TODO: Remove in favor of Scheduler.next514 return runWithPriority(NormalPriority, fn);515}516export function interactiveUpdates<A, B, C, R>(517 fn: (A, B, C) => R,518 a: A,519 b: B,520 c: C,521): R {522 if (workPhase === NotWorking) {523 // TODO: Remove this call. Instead of doing this automatically, the caller524 // should explicitly call flushInteractiveUpdates.525 flushPendingDiscreteUpdates();526 }527 return runWithPriority(UserBlockingPriority, fn.bind(null, a, b, c));528}529export function syncUpdates<A, B, C, R>(530 fn: (A, B, C) => R,531 a: A,532 b: B,533 c: C,534): R {535 return runWithPriority(ImmediatePriority, fn.bind(null, a, b, c));536}537function flushPendingDiscreteUpdates() {538 if (rootsWithPendingDiscreteUpdates !== null) {539 // For each root with pending discrete updates, schedule a callback to540 // immediately flush them.541 const roots = rootsWithPendingDiscreteUpdates;542 rootsWithPendingDiscreteUpdates = null;543 roots.forEach((expirationTime, root) => {544 scheduleCallback(545 ImmediatePriority,546 renderRoot.bind(null, root, expirationTime),547 );548 });549 // Now flush the immediate queue.550 flushImmediateQueue();551 }552}553export function batchedUpdates<A, R>(fn: A => R, a: A): R {554 if (workPhase !== NotWorking) {555 // We're already working, or inside a batch, so batchedUpdates is a no-op.556 return fn(a);557 }558 workPhase = BatchedPhase;559 try {560 return fn(a);561 } finally {562 workPhase = NotWorking;563 // Flush the immediate callbacks that were scheduled during this batch564 flushImmediateQueue();565 }566}567export function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {568 if (workPhase !== BatchedPhase && workPhase !== FlushSyncPhase) {569 // We're not inside batchedUpdates or flushSync, so unbatchedUpdates is570 // a no-op.571 return fn(a);572 }573 const prevWorkPhase = workPhase;574 workPhase = LegacyUnbatchedPhase;575 try {576 return fn(a);577 } finally {578 workPhase = prevWorkPhase;579 }580}581export function flushSync<A, R>(fn: A => R, a: A): R {582 if (workPhase === RenderPhase || workPhase === CommitPhase) {583 invariant(584 false,585 'flushSync was called from inside a lifecycle method. It cannot be ' +586 'called when React is already rendering.',587 );588 }589 const prevWorkPhase = workPhase;590 workPhase = FlushSyncPhase;591 try {592 return runWithPriority(ImmediatePriority, fn.bind(null, a));593 } finally {594 workPhase = prevWorkPhase;595 // Flush the immediate callbacks that were scheduled during this batch.596 // Note that this will happen even if batchedUpdates is higher up597 // the stack.598 flushImmediateQueue();599 }600}601export function flushControlled(fn: () => mixed): void {602 const prevWorkPhase = workPhase;603 workPhase = BatchedPhase;604 try {605 runWithPriority(ImmediatePriority, fn);606 } finally {607 workPhase = prevWorkPhase;608 if (workPhase === NotWorking) {609 // Flush the immediate callbacks that were scheduled during this batch610 flushImmediateQueue();611 }612 }613}614function prepareFreshStack(root, expirationTime) {615 root.pendingCommitExpirationTime = NoWork;616 if (workInProgress !== null) {617 let interruptedWork = workInProgress.return;618 while (interruptedWork !== null) {619 unwindInterruptedWork(interruptedWork);620 interruptedWork = interruptedWork.return;621 }622 }623 workInProgressRoot = root;624 workInProgress = createWorkInProgress(root.current, null, expirationTime);625 renderExpirationTime = expirationTime;626 workInProgressRootExitStatus = RootIncomplete;627 workInProgressRootAbsoluteTimeoutMs = -1;628 if (__DEV__) {629 ReactStrictModeWarnings.discardPendingWarnings();630 }631}632function renderRoot(633 root: FiberRoot,634 expirationTime: ExpirationTime,635 isSync: boolean,636): SchedulerCallback | null {637 invariant(638 workPhase !== RenderPhase && workPhase !== CommitPhase,639 'Should not already be working.',640 );641 if (enableUserTimingAPI && expirationTime !== Sync) {642 const didExpire = isSync;643 const timeoutMs = expirationTimeToMs(expirationTime);644 stopRequestCallbackTimer(didExpire, timeoutMs);645 }646 if (root.firstPendingTime < expirationTime) {647 // If there's no work left at this expiration time, exit immediately. This648 // happens when multiple callbacks are scheduled for a single root, but an649 // earlier callback flushes the work of a later one.650 return null;651 }652 if (root.pendingCommitExpirationTime === expirationTime) {653 // There's already a pending commit at this expiration time.654 root.pendingCommitExpirationTime = NoWork;655 return commitRoot.bind(null, root, expirationTime);656 }657 flushPassiveEffects();658 // If the root or expiration time have changed, throw out the existing stack659 // and prepare a fresh one. Otherwise we'll continue where we left off.660 if (root !== workInProgressRoot || expirationTime !== renderExpirationTime) {661 prepareFreshStack(root, expirationTime);662 startWorkOnPendingInteraction(root, expirationTime);663 }664 // If we have a work-in-progress fiber, it means there's still work to do665 // in this root.666 if (workInProgress !== null) {667 const prevWorkPhase = workPhase;668 workPhase = RenderPhase;669 let prevDispatcher = ReactCurrentDispatcher.current;670 if (prevDispatcher === null) {671 // The React isomorphic package does not include a default dispatcher.672 // Instead the first renderer will lazily attach one, in order to give673 // nicer error messages.674 prevDispatcher = ContextOnlyDispatcher;675 }676 ReactCurrentDispatcher.current = ContextOnlyDispatcher;677 let prevInteractions: Set<Interaction> | null = null;678 if (enableSchedulerTracing) {679 prevInteractions = __interactionsRef.current;680 __interactionsRef.current = root.memoizedInteractions;681 }682 startWorkLoopTimer(workInProgress);683 // TODO: Fork renderRoot into renderRootSync and renderRootAsync684 if (isSync) {685 if (expirationTime !== Sync) {686 // An async update expired. There may be other expired updates on687 // this root. We should render all the expired work in a688 // single batch.689 const currentTime = requestCurrentTime();690 if (currentTime < expirationTime) {691 // Restart at the current time.692 workPhase = prevWorkPhase;693 resetContextDependencies();694 ReactCurrentDispatcher.current = prevDispatcher;695 if (enableSchedulerTracing) {696 __interactionsRef.current = ((prevInteractions: any): Set<697 Interaction,698 >);699 }700 return renderRoot.bind(null, root, currentTime);701 }702 }703 } else {704 // Since we know we're in a React event, we can clear the current705 // event time. The next update will compute a new event time.706 currentEventTime = NoWork;707 }708 do {709 try {710 if (isSync) {711 workLoopSync();712 } else {713 workLoop();714 }715 break;716 } catch (thrownValue) {717 // Reset module-level state that was set during the render phase.718 resetContextDependencies();719 resetHooks();720 const sourceFiber = workInProgress;721 if (sourceFiber === null || sourceFiber.return === null) {722 // Expected to be working on a non-root fiber. This is a fatal error723 // because there's no ancestor that can handle it; the root is724 // supposed to capture all errors that weren't caught by an error725 // boundary.726 prepareFreshStack(root, expirationTime);727 workPhase = prevWorkPhase;728 throw thrownValue;729 }730 if (enableProfilerTimer && sourceFiber.mode & ProfileMode) {731 // Record the time spent rendering before an error was thrown. This732 // avoids inaccurate Profiler durations in the case of a733 // suspended render.734 stopProfilerTimerIfRunningAndRecordDelta(sourceFiber, true);735 }736 const returnFiber = sourceFiber.return;737 throwException(738 root,739 returnFiber,740 sourceFiber,741 thrownValue,742 renderExpirationTime,743 );744 workInProgress = completeUnitOfWork(sourceFiber);745 }746 } while (true);747 workPhase = prevWorkPhase;748 resetContextDependencies();749 ReactCurrentDispatcher.current = prevDispatcher;750 if (enableSchedulerTracing) {751 __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);752 }753 if (workInProgress !== null) {754 // There's still work left over. Return a continuation.755 stopInterruptedWorkLoopTimer();756 if (expirationTime !== Sync) {757 startRequestCallbackTimer();758 }759 return renderRoot.bind(null, root, expirationTime);760 }761 }762 // We now have a consistent tree. The next step is either to commit it, or, if763 // something suspended, wait to commit it after a timeout.764 stopFinishedWorkLoopTimer();765 const isLocked = resolveLocksOnRoot(root, expirationTime);766 if (isLocked) {767 // This root has a lock that prevents it from committing. Exit. If we begin768 // work on the root again, without any intervening updates, it will finish769 // without doing additional work.770 return null;771 }772 // Set this to null to indicate there's no in-progress render.773 workInProgressRoot = null;774 switch (workInProgressRootExitStatus) {775 case RootIncomplete: {776 invariant(false, 'Should have a work-in-progress.');777 }778 // Flow knows about invariant, so it compains if I add a break statement,779 // but eslint doesn't know about invariant, so it complains if I do.780 // eslint-disable-next-line no-fallthrough781 case RootErrored: {782 // An error was thrown. First check if there is lower priority work783 // scheduled on this root.784 const lastPendingTime = root.lastPendingTime;785 if (root.lastPendingTime < expirationTime) {786 // There's lower priority work. Before raising the error, try rendering787 // at the lower priority to see if it fixes it. Use a continuation to788 // maintain the existing priority and position in the queue.789 return renderRoot.bind(null, root, lastPendingTime);790 }791 if (!isSync) {792 // If we're rendering asynchronously, it's possible the error was793 // caused by tearing due to a mutation during an event. Try rendering794 // one more time without yiedling to events.795 prepareFreshStack(root, expirationTime);796 scheduleCallback(797 ImmediatePriority,798 renderRoot.bind(null, root, expirationTime),799 );800 return null;801 }802 // If we're already rendering synchronously, commit the root in its803 // errored state.804 return commitRoot.bind(null, root, expirationTime);805 }806 case RootSuspended: {807 const lastPendingTime = root.lastPendingTime;808 if (root.lastPendingTime < expirationTime) {809 // There's lower priority work. It might be unsuspended. Try rendering810 // at that level.811 return renderRoot.bind(null, root, lastPendingTime);812 }813 if (!isSync) {814 const msUntilTimeout = computeMsUntilTimeout(815 root,816 workInProgressRootAbsoluteTimeoutMs,817 );818 if (msUntilTimeout > 0) {819 // The render is suspended, it hasn't timed out, and there's no lower820 // priority work to do. Instead of committing the fallback821 // immediately, wait for more data to arrive.822 root.timeoutHandle = scheduleTimeout(823 commitRoot.bind(null, root, expirationTime),824 msUntilTimeout,825 );826 return null;827 }828 }829 // The work expired. Commit immediately.830 return commitRoot.bind(null, root, expirationTime);831 }832 case RootCompleted: {833 // The work completed. Ready to commit.834 return commitRoot.bind(null, root, expirationTime);835 }836 default: {837 invariant(false, 'Unknown root exit status.');838 }839 }840}841export function renderDidSuspend(842 root: FiberRoot,843 absoluteTimeoutMs: number,844 // TODO: Don't need this argument anymore845 suspendedTime: ExpirationTime,846) {847 if (848 absoluteTimeoutMs >= 0 &&849 workInProgressRootAbsoluteTimeoutMs < absoluteTimeoutMs850 ) {851 workInProgressRootAbsoluteTimeoutMs = absoluteTimeoutMs;852 if (workInProgressRootExitStatus === RootIncomplete) {853 workInProgressRootExitStatus = RootSuspended;854 }855 }856}857export function renderDidError() {858 if (859 workInProgressRootExitStatus === RootIncomplete ||860 workInProgressRootExitStatus === RootSuspended861 ) {862 workInProgressRootExitStatus = RootErrored;863 }864}865function workLoopSync() {866 // Already timed out, so perform work without checking if we need to yield.867 while (workInProgress !== null) {868 workInProgress = performUnitOfWork(workInProgress);869 }870}871function workLoop() {872 // Perform work until Scheduler asks us to yield873 while (workInProgress !== null && !shouldYield()) {874 workInProgress = performUnitOfWork(workInProgress);875 }876}877function performUnitOfWork(unitOfWork: Fiber): Fiber | null {878 // The current, flushed, state of this fiber is the alternate. Ideally879 // nothing should rely on this, but relying on it here means that we don't880 // need an additional field on the work in progress.881 const current = unitOfWork.alternate;882 startWorkTimer(unitOfWork);883 setCurrentDebugFiberInDEV(unitOfWork);884 let next;885 if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoContext) {886 startProfilerTimer(unitOfWork);887 next = beginWork(current, unitOfWork, renderExpirationTime);888 stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);889 } else {890 next = beginWork(current, unitOfWork, renderExpirationTime);891 }892 resetCurrentDebugFiberInDEV();893 unitOfWork.memoizedProps = unitOfWork.pendingProps;894 if (next === null) {895 // If this doesn't spawn new work, complete the current work.896 next = completeUnitOfWork(unitOfWork);897 }898 ReactCurrentOwner.current = null;899 return next;900}901function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {902 // Attempt to complete the current unit of work, then move to the next903 // sibling. If there are no more siblings, return to the parent fiber.904 workInProgress = unitOfWork;905 do {906 // The current, flushed, state of this fiber is the alternate. Ideally907 // nothing should rely on this, but relying on it here means that we don't908 // need an additional field on the work in progress.909 const current = workInProgress.alternate;910 const returnFiber = workInProgress.return;911 // Check if the work completed or if something threw.912 if ((workInProgress.effectTag & Incomplete) === NoEffect) {913 setCurrentDebugFiberInDEV(workInProgress);914 let next;915 if (916 !enableProfilerTimer ||917 (workInProgress.mode & ProfileMode) === NoContext918 ) {919 next = completeWork(current, workInProgress, renderExpirationTime);920 } else {921 startProfilerTimer(workInProgress);922 next = completeWork(current, workInProgress, renderExpirationTime);923 // Update render duration assuming we didn't error.924 stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);925 }926 stopWorkTimer(workInProgress);927 resetCurrentDebugFiberInDEV();928 resetChildExpirationTime(workInProgress);929 if (next !== null) {930 // Completing this fiber spawned new work. Work on that next.931 return next;932 }933 if (934 returnFiber !== null &&935 // Do not append effects to parents if a sibling failed to complete936 (returnFiber.effectTag & Incomplete) === NoEffect937 ) {938 // Append all the effects of the subtree and this fiber onto the effect939 // list of the parent. The completion order of the children affects the940 // side-effect order.941 if (returnFiber.firstEffect === null) {942 returnFiber.firstEffect = workInProgress.firstEffect;943 }944 if (workInProgress.lastEffect !== null) {945 if (returnFiber.lastEffect !== null) {946 returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;947 }948 returnFiber.lastEffect = workInProgress.lastEffect;949 }950 // If this fiber had side-effects, we append it AFTER the children's951 // side-effects. We can perform certain side-effects earlier if needed,952 // by doing multiple passes over the effect list. We don't want to953 // schedule our own side-effect on our own list because if end up954 // reusing children we'll schedule this effect onto itself since we're955 // at the end.956 const effectTag = workInProgress.effectTag;957 // Skip both NoWork and PerformedWork tags when creating the effect958 // list. PerformedWork effect is read by React DevTools but shouldn't be959 // committed.960 if (effectTag > PerformedWork) {961 if (returnFiber.lastEffect !== null) {962 returnFiber.lastEffect.nextEffect = workInProgress;963 } else {964 returnFiber.firstEffect = workInProgress;965 }966 returnFiber.lastEffect = workInProgress;967 }968 }969 } else {970 // This fiber did not complete because something threw. Pop values off971 // the stack without entering the complete phase. If this is a boundary,972 // capture values if possible.973 const next = unwindWork(workInProgress, renderExpirationTime);974 // Because this fiber did not complete, don't reset its expiration time.975 if (976 enableProfilerTimer &&977 (workInProgress.mode & ProfileMode) !== NoContext978 ) {979 // Record the render duration for the fiber that errored.980 stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);981 // Include the time spent working on failed children before continuing.982 let actualDuration = workInProgress.actualDuration;983 let child = workInProgress.child;984 while (child !== null) {985 actualDuration += child.actualDuration;986 child = child.sibling;987 }988 workInProgress.actualDuration = actualDuration;989 }990 if (next !== null) {991 // If completing this work spawned new work, do that next. We'll come992 // back here again.993 // Since we're restarting, remove anything that is not a host effect994 // from the effect tag.995 // TODO: The name stopFailedWorkTimer is misleading because Suspense996 // also captures and restarts.997 stopFailedWorkTimer(workInProgress);998 next.effectTag &= HostEffectMask;999 return next;1000 }1001 stopWorkTimer(workInProgress);1002 if (returnFiber !== null) {1003 // Mark the parent fiber as incomplete and clear its effect list.1004 returnFiber.firstEffect = returnFiber.lastEffect = null;1005 returnFiber.effectTag |= Incomplete;1006 }1007 }1008 const siblingFiber = workInProgress.sibling;1009 if (siblingFiber !== null) {1010 // If there is more work to do in this returnFiber, do that next.1011 return siblingFiber;1012 }1013 // Otherwise, return to the parent1014 workInProgress = returnFiber;1015 } while (workInProgress !== null);1016 // We've reached the root.1017 if (workInProgressRootExitStatus === RootIncomplete) {1018 workInProgressRootExitStatus = RootCompleted;1019 }1020 return null;1021}1022function resetChildExpirationTime(completedWork: Fiber) {1023 if (1024 renderExpirationTime !== Never &&1025 completedWork.childExpirationTime === Never1026 ) {1027 // The children of this component are hidden. Don't bubble their1028 // expiration times.1029 return;1030 }1031 let newChildExpirationTime = NoWork;1032 // Bubble up the earliest expiration time.1033 if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoContext) {1034 // In profiling mode, resetChildExpirationTime is also used to reset1035 // profiler durations.1036 let actualDuration = completedWork.actualDuration;1037 let treeBaseDuration = completedWork.selfBaseDuration;1038 // When a fiber is cloned, its actualDuration is reset to 0. This value will1039 // only be updated if work is done on the fiber (i.e. it doesn't bailout).1040 // When work is done, it should bubble to the parent's actualDuration. If1041 // the fiber has not been cloned though, (meaning no work was done), then1042 // this value will reflect the amount of time spent working on a previous1043 // render. In that case it should not bubble. We determine whether it was1044 // cloned by comparing the child pointer.1045 const shouldBubbleActualDurations =1046 completedWork.alternate === null ||1047 completedWork.child !== completedWork.alternate.child;1048 let child = completedWork.child;1049 while (child !== null) {1050 const childUpdateExpirationTime = child.expirationTime;1051 const childChildExpirationTime = child.childExpirationTime;1052 if (childUpdateExpirationTime > newChildExpirationTime) {1053 newChildExpirationTime = childUpdateExpirationTime;1054 }1055 if (childChildExpirationTime > newChildExpirationTime) {1056 newChildExpirationTime = childChildExpirationTime;1057 }1058 if (shouldBubbleActualDurations) {1059 actualDuration += child.actualDuration;1060 }1061 treeBaseDuration += child.treeBaseDuration;1062 child = child.sibling;1063 }1064 completedWork.actualDuration = actualDuration;1065 completedWork.treeBaseDuration = treeBaseDuration;1066 } else {1067 let child = completedWork.child;1068 while (child !== null) {1069 const childUpdateExpirationTime = child.expirationTime;1070 const childChildExpirationTime = child.childExpirationTime;1071 if (childUpdateExpirationTime > newChildExpirationTime) {1072 newChildExpirationTime = childUpdateExpirationTime;1073 }1074 if (childChildExpirationTime > newChildExpirationTime) {1075 newChildExpirationTime = childChildExpirationTime;1076 }1077 child = child.sibling;1078 }1079 }1080 completedWork.childExpirationTime = newChildExpirationTime;1081}1082function commitRoot(root, expirationTime) {1083 runWithPriority(1084 ImmediatePriority,1085 commitRootImpl.bind(null, root, expirationTime),1086 );1087 // If there are passive effects, schedule a callback to flush them. This goes1088 // outside commitRootImpl so that it inherits the priority of the render.1089 if (rootWithPendingPassiveEffects !== null) {1090 const priorityLevel = getCurrentPriorityLevel();1091 scheduleCallback(priorityLevel, () => {1092 flushPassiveEffects();1093 return null;1094 });1095 }1096 return null;1097}1098function commitRootImpl(root, expirationTime) {1099 flushPassiveEffects();1100 flushRenderPhaseStrictModeWarningsInDEV();1101 invariant(1102 workPhase !== RenderPhase && workPhase !== CommitPhase,1103 'Should not already be working.',1104 );1105 const finishedWork = root.current.alternate;1106 invariant(finishedWork !== null, 'Should have a work-in-progress root.');1107 // commitRoot never returns a continuation; it always finishes synchronously.1108 // So we can clear these now to allow a new callback to be scheduled.1109 root.callbackNode = null;1110 root.callbackExpirationTime = NoWork;1111 startCommitTimer();1112 // Update the first and last pending times on this root. The new first1113 // pending time is whatever is left on the root fiber.1114 const updateExpirationTimeBeforeCommit = finishedWork.expirationTime;1115 const childExpirationTimeBeforeCommit = finishedWork.childExpirationTime;1116 const firstPendingTimeBeforeCommit =1117 childExpirationTimeBeforeCommit > updateExpirationTimeBeforeCommit1118 ? childExpirationTimeBeforeCommit1119 : updateExpirationTimeBeforeCommit;1120 root.firstPendingTime = firstPendingTimeBeforeCommit;1121 if (firstPendingTimeBeforeCommit < root.lastPendingTime) {1122 // This usually means we've finished all the work, but it can also happen1123 // when something gets downprioritized during render, like a hidden tree.1124 root.lastPendingTime = firstPendingTimeBeforeCommit;1125 }1126 if (root === workInProgressRoot) {1127 // We can reset these now that they are finished.1128 workInProgressRoot = null;1129 workInProgress = null;1130 renderExpirationTime = NoWork;1131 } else {1132 // This indicates that the last root we worked on is not the same one that1133 // we're committing now. This most commonly happens when a suspended root1134 // times out.1135 }1136 // Get the list of effects.1137 let firstEffect;1138 if (finishedWork.effectTag > PerformedWork) {1139 // A fiber's effect list consists only of its children, not itself. So if1140 // the root has an effect, we need to add it to the end of the list. The1141 // resulting list is the set that would belong to the root's parent, if it1142 // had one; that is, all the effects in the tree including the root.1143 if (finishedWork.lastEffect !== null) {1144 finishedWork.lastEffect.nextEffect = finishedWork;1145 firstEffect = finishedWork.firstEffect;1146 } else {1147 firstEffect = finishedWork;1148 }1149 } else {1150 // There is no effect on the root.1151 firstEffect = finishedWork.firstEffect;1152 }1153 if (firstEffect !== null) {1154 const prevWorkPhase = workPhase;1155 workPhase = CommitPhase;1156 let prevInteractions: Set<Interaction> | null = null;1157 if (enableSchedulerTracing) {1158 prevInteractions = __interactionsRef.current;1159 __interactionsRef.current = root.memoizedInteractions;1160 }1161 // Reset this to null before calling lifecycles1162 ReactCurrentOwner.current = null;1163 // The commit phase is broken into several sub-phases. We do a separate pass1164 // of the effect list for each phase: all mutation effects come before all1165 // layout effects, and so on.1166 // The first phase a "before mutation" phase. We use this phase to read the1167 // state of the host tree right before we mutate it. This is where1168 // getSnapshotBeforeUpdate is called.1169 startCommitSnapshotEffectsTimer();1170 prepareForCommit(root.containerInfo);1171 nextEffect = firstEffect;1172 do {1173 if (__DEV__) {1174 invokeGuardedCallback(null, commitBeforeMutationEffects, null);1175 if (hasCaughtError()) {1176 invariant(nextEffect !== null, 'Should be working on an effect.');1177 const error = clearCaughtError();1178 captureCommitPhaseError(nextEffect, error);1179 nextEffect = nextEffect.nextEffect;1180 }1181 } else {1182 try {1183 commitBeforeMutationEffects();1184 } catch (error) {1185 invariant(nextEffect !== null, 'Should be working on an effect.');1186 captureCommitPhaseError(nextEffect, error);1187 nextEffect = nextEffect.nextEffect;1188 }1189 }1190 } while (nextEffect !== null);1191 stopCommitSnapshotEffectsTimer();1192 if (enableProfilerTimer) {1193 // Mark the current commit time to be shared by all Profilers in this1194 // batch. This enables them to be grouped later.1195 recordCommitTime();1196 }1197 // The next phase is the mutation phase, where we mutate the host tree.1198 startCommitHostEffectsTimer();1199 nextEffect = firstEffect;1200 do {1201 if (__DEV__) {1202 invokeGuardedCallback(null, commitMutationEffects, null);1203 if (hasCaughtError()) {1204 invariant(nextEffect !== null, 'Should be working on an effect.');1205 const error = clearCaughtError();1206 captureCommitPhaseError(nextEffect, error);1207 nextEffect = nextEffect.nextEffect;1208 }1209 } else {1210 try {1211 commitMutationEffects();1212 } catch (error) {1213 invariant(nextEffect !== null, 'Should be working on an effect.');1214 captureCommitPhaseError(nextEffect, error);1215 nextEffect = nextEffect.nextEffect;1216 }1217 }1218 } while (nextEffect !== null);1219 stopCommitHostEffectsTimer();1220 resetAfterCommit(root.containerInfo);1221 // The work-in-progress tree is now the current tree. This must come after1222 // the mutation phase, so that the previous tree is still current during1223 // componentWillUnmount, but before the layout phase, so that the finished1224 // work is current during componentDidMount/Update.1225 root.current = finishedWork;1226 // The next phase is the layout phase, where we call effects that read1227 // the host tree after it's been mutated. The idiomatic use case for this is1228 // layout, but class component lifecycles also fire here for legacy reasons.1229 startCommitLifeCyclesTimer();1230 nextEffect = firstEffect;1231 do {1232 if (__DEV__) {1233 invokeGuardedCallback(1234 null,1235 commitLayoutEffects,1236 null,1237 root,1238 expirationTime,1239 );1240 if (hasCaughtError()) {1241 invariant(nextEffect !== null, 'Should be working on an effect.');1242 const error = clearCaughtError();1243 captureCommitPhaseError(nextEffect, error);1244 nextEffect = nextEffect.nextEffect;1245 }1246 } else {1247 try {1248 commitLayoutEffects(root, expirationTime);1249 } catch (error) {1250 invariant(nextEffect !== null, 'Should be working on an effect.');1251 captureCommitPhaseError(nextEffect, error);1252 nextEffect = nextEffect.nextEffect;1253 }1254 }1255 } while (nextEffect !== null);1256 stopCommitLifeCyclesTimer();1257 nextEffect = null;1258 if (enableSchedulerTracing) {1259 __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1260 }1261 workPhase = prevWorkPhase;1262 } else {1263 // No effects.1264 root.current = finishedWork;1265 // Measure these anyway so the flamegraph explicitly shows that there were1266 // no effects.1267 // TODO: Maybe there's a better way to report this.1268 startCommitSnapshotEffectsTimer();1269 stopCommitSnapshotEffectsTimer();1270 if (enableProfilerTimer) {1271 recordCommitTime();1272 }1273 startCommitHostEffectsTimer();1274 stopCommitHostEffectsTimer();1275 startCommitLifeCyclesTimer();1276 stopCommitLifeCyclesTimer();1277 }1278 stopCommitTimer();1279 if (rootDoesHavePassiveEffects) {1280 // This commit has passive effects. Stash a reference to them. But don't1281 // schedule a callback until after flushing layout work.1282 rootDoesHavePassiveEffects = false;1283 rootWithPendingPassiveEffects = root;1284 pendingPassiveEffectsExpirationTime = expirationTime;1285 } else {1286 if (enableSchedulerTracing) {1287 // If there are no passive effects, then we can complete the pending1288 // interactions. Otherwise, we'll wait until after the passive effects1289 // are flushed.1290 finishPendingInteractions(root, expirationTime);1291 }1292 }1293 // Check if there's remaining work on this root1294 const remainingExpirationTime = root.firstPendingTime;1295 if (remainingExpirationTime !== NoWork) {1296 const currentTime = requestCurrentTime();1297 const priorityLevel = inferPriorityFromExpirationTime(1298 currentTime,1299 remainingExpirationTime,1300 );1301 scheduleCallbackForRoot(root, priorityLevel, remainingExpirationTime);1302 } else {1303 // If there's no remaining work, we can clear the set of already failed1304 // error boundaries.1305 legacyErrorBoundariesThatAlreadyFailed = null;1306 }1307 onCommitRoot(finishedWork.stateNode);1308 if (remainingExpirationTime === Sync) {1309 // Count the number of times the root synchronously re-renders without1310 // finishing. If there are too many, it indicates an infinite update loop.1311 if (root === rootWithNestedUpdates) {1312 nestedUpdateCount++;1313 } else {1314 nestedUpdateCount = 0;1315 rootWithNestedUpdates = root;1316 }1317 } else {1318 nestedUpdateCount = 0;1319 }1320 if (hasUncaughtError) {1321 hasUncaughtError = false;1322 const error = firstUncaughtError;1323 firstUncaughtError = null;1324 throw error;1325 }1326 if (workPhase === LegacyUnbatchedPhase) {1327 // This is a legacy edge case. We just committed the initial mount of1328 // a ReactDOM.render-ed root inside of batchedUpdates. The commit fired1329 // synchronously, but layout updates should be deferred until the end1330 // of the batch.1331 return null;1332 }1333 // If layout work was scheduled, flush it now.1334 flushImmediateQueue();1335 return null;1336}1337function commitBeforeMutationEffects() {1338 while (nextEffect !== null) {1339 if ((nextEffect.effectTag & Snapshot) !== NoEffect) {1340 setCurrentDebugFiberInDEV(nextEffect);1341 recordEffect();1342 const current = nextEffect.alternate;1343 commitBeforeMutationEffectOnFiber(current, nextEffect);1344 resetCurrentDebugFiberInDEV();1345 }1346 nextEffect = nextEffect.nextEffect;1347 }1348}1349function commitMutationEffects() {1350 // TODO: Should probably move the bulk of this function to commitWork.1351 while (nextEffect !== null) {1352 setCurrentDebugFiberInDEV(nextEffect);1353 const effectTag = nextEffect.effectTag;1354 if (effectTag & ContentReset) {1355 commitResetTextContent(nextEffect);1356 }1357 if (effectTag & Ref) {1358 const current = nextEffect.alternate;1359 if (current !== null) {1360 commitDetachRef(current);1361 }1362 }1363 // The following switch statement is only concerned about placement,1364 // updates, and deletions. To avoid needing to add a case for every possible1365 // bitmap value, we remove the secondary effects from the effect tag and1366 // switch on that value.1367 let primaryEffectTag = effectTag & (Placement | Update | Deletion);1368 switch (primaryEffectTag) {1369 case Placement: {1370 commitPlacement(nextEffect);1371 // Clear the "placement" from effect tag so that we know that this is1372 // inserted, before any life-cycles like componentDidMount gets called.1373 // TODO: findDOMNode doesn't rely on this any more but isMounted does1374 // and isMounted is deprecated anyway so we should be able to kill this.1375 nextEffect.effectTag &= ~Placement;1376 break;1377 }1378 case PlacementAndUpdate: {1379 // Placement1380 commitPlacement(nextEffect);1381 // Clear the "placement" from effect tag so that we know that this is1382 // inserted, before any life-cycles like componentDidMount gets called.1383 nextEffect.effectTag &= ~Placement;1384 // Update1385 const current = nextEffect.alternate;1386 commitWork(current, nextEffect);1387 break;1388 }1389 case Update: {1390 const current = nextEffect.alternate;1391 commitWork(current, nextEffect);1392 break;1393 }1394 case Deletion: {1395 commitDeletion(nextEffect);1396 break;1397 }1398 }1399 // TODO: Only record a mutation effect if primaryEffectTag is non-zero.1400 recordEffect();1401 resetCurrentDebugFiberInDEV();1402 nextEffect = nextEffect.nextEffect;1403 }1404}1405function commitLayoutEffects(1406 root: FiberRoot,1407 committedExpirationTime: ExpirationTime,1408) {1409 // TODO: Should probably move the bulk of this function to commitWork.1410 while (nextEffect !== null) {1411 setCurrentDebugFiberInDEV(nextEffect);1412 const effectTag = nextEffect.effectTag;1413 if (effectTag & (Update | Callback)) {1414 recordEffect();1415 const current = nextEffect.alternate;1416 commitLayoutEffectOnFiber(1417 root,1418 current,1419 nextEffect,1420 committedExpirationTime,1421 );1422 }1423 if (effectTag & Ref) {1424 recordEffect();1425 commitAttachRef(nextEffect);1426 }1427 if (effectTag & Passive) {1428 rootDoesHavePassiveEffects = true;1429 }1430 resetCurrentDebugFiberInDEV();1431 nextEffect = nextEffect.nextEffect;1432 }1433}1434export function flushPassiveEffects() {1435 if (rootWithPendingPassiveEffects === null) {1436 return false;1437 }1438 const root = rootWithPendingPassiveEffects;1439 const expirationTime = pendingPassiveEffectsExpirationTime;1440 rootWithPendingPassiveEffects = null;1441 pendingPassiveEffectsExpirationTime = NoWork;1442 let prevInteractions: Set<Interaction> | null = null;1443 if (enableSchedulerTracing) {1444 prevInteractions = __interactionsRef.current;1445 __interactionsRef.current = root.memoizedInteractions;1446 }1447 invariant(1448 workPhase !== RenderPhase && workPhase !== CommitPhase,1449 'Cannot flush passive effects while already rendering.',1450 );1451 const prevWorkPhase = workPhase;1452 workPhase = CommitPhase;1453 // Note: This currently assumes there are no passive effects on the root1454 // fiber, because the root is not part of its own effect list. This could1455 // change in the future.1456 let effect = root.current.firstEffect;1457 while (effect !== null) {1458 if (__DEV__) {1459 setCurrentDebugFiberInDEV(effect);1460 invokeGuardedCallback(null, commitPassiveHookEffects, null, effect);1461 if (hasCaughtError()) {1462 invariant(effect !== null, 'Should be working on an effect.');1463 const error = clearCaughtError();1464 captureCommitPhaseError(effect, error);1465 }1466 resetCurrentDebugFiberInDEV();1467 } else {1468 try {1469 commitPassiveHookEffects(effect);1470 } catch (error) {1471 invariant(effect !== null, 'Should be working on an effect.');1472 captureCommitPhaseError(effect, error);1473 }1474 }1475 effect = effect.nextEffect;1476 }1477 if (enableSchedulerTracing) {1478 __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1479 finishPendingInteractions(root, expirationTime);1480 }1481 workPhase = prevWorkPhase;1482 flushImmediateQueue();1483 // If additional passive effects were scheduled, increment a counter. If this1484 // exceeds the limit, we'll fire a warning.1485 nestedPassiveUpdateCount =1486 rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;1487 return true;1488}1489export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {1490 return (1491 legacyErrorBoundariesThatAlreadyFailed !== null &&1492 legacyErrorBoundariesThatAlreadyFailed.has(instance)1493 );1494}1495export function markLegacyErrorBoundaryAsFailed(instance: mixed) {1496 if (legacyErrorBoundariesThatAlreadyFailed === null) {1497 legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);1498 } else {1499 legacyErrorBoundariesThatAlreadyFailed.add(instance);1500 }1501}1502function prepareToThrowUncaughtError(error: mixed) {1503 if (!hasUncaughtError) {1504 hasUncaughtError = true;1505 firstUncaughtError = error;1506 }1507}1508export const onUncaughtError = prepareToThrowUncaughtError;1509function captureCommitPhaseErrorOnRoot(1510 rootFiber: Fiber,1511 sourceFiber: Fiber,1512 error: mixed,1513) {1514 const errorInfo = createCapturedValue(error, sourceFiber);1515 const update = createRootErrorUpdate(rootFiber, errorInfo, Sync);1516 enqueueUpdate(rootFiber, update);1517 const root = markUpdateTimeFromFiberToRoot(rootFiber, Sync);1518 if (root !== null) {1519 scheduleCallbackForRoot(root, ImmediatePriority, Sync);1520 }1521}1522export function captureCommitPhaseError(sourceFiber: Fiber, error: mixed) {1523 if (sourceFiber.tag === HostRoot) {1524 // Error was thrown at the root. There is no parent, so the root1525 // itself should capture it.1526 captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);1527 return;1528 }1529 let fiber = sourceFiber.return;1530 while (fiber !== null) {1531 if (fiber.tag === HostRoot) {1532 captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);1533 return;1534 } else if (fiber.tag === ClassComponent) {1535 const ctor = fiber.type;1536 const instance = fiber.stateNode;1537 if (1538 typeof ctor.getDerivedStateFromError === 'function' ||1539 (typeof instance.componentDidCatch === 'function' &&1540 !isAlreadyFailedLegacyErrorBoundary(instance))1541 ) {1542 const errorInfo = createCapturedValue(error, sourceFiber);1543 const update = createClassErrorUpdate(1544 fiber,1545 errorInfo,1546 // TODO: This is always sync1547 Sync,1548 );1549 enqueueUpdate(fiber, update);1550 const root = markUpdateTimeFromFiberToRoot(fiber, Sync);1551 if (root !== null) {1552 scheduleCallbackForRoot(root, ImmediatePriority, Sync);1553 }1554 return;1555 }1556 }1557 fiber = fiber.return;1558 }1559}1560export function pingSuspendedRoot(1561 root: FiberRoot,1562 thenable: Thenable,1563 suspendedTime: ExpirationTime,1564) {1565 const pingCache = root.pingCache;1566 if (pingCache !== null) {1567 // The thenable resolved, so we no longer need to memoize, because it will1568 // never be thrown again.1569 pingCache.delete(thenable);1570 }1571 if (workInProgressRoot === root && renderExpirationTime === suspendedTime) {1572 // Received a ping at the same priority level at which we're currently1573 // rendering. Restart from the root. Don't need to schedule a ping because1574 // we're already working on this tree.1575 prepareFreshStack(root, renderExpirationTime);1576 return;1577 }1578 const lastPendingTime = root.lastPendingTime;1579 if (lastPendingTime < suspendedTime) {1580 // The root is no longer suspended at this time.1581 return;1582 }1583 const pingTime = root.pingTime;1584 if (pingTime !== NoWork && pingTime < suspendedTime) {1585 // There's already a lower priority ping scheduled.1586 return;1587 }1588 // Mark the time at which this ping was scheduled.1589 root.pingTime = suspendedTime;1590 const currentTime = requestCurrentTime();1591 const priorityLevel = inferPriorityFromExpirationTime(1592 currentTime,1593 suspendedTime,1594 );1595 scheduleCallbackForRoot(root, priorityLevel, suspendedTime);1596}1597export function retryTimedOutBoundary(boundaryFiber: Fiber) {1598 // The boundary fiber (a Suspense component) previously timed out and was1599 // rendered in its fallback state. One of the promises that suspended it has1600 // resolved, which means at least part of the tree was likely unblocked. Try1601 // rendering again, at a new expiration time.1602 const currentTime = requestCurrentTime();1603 const retryTime = computeExpirationForFiber(currentTime, boundaryFiber);1604 // TODO: Special case idle priority?1605 const priorityLevel = inferPriorityFromExpirationTime(currentTime, retryTime);1606 const root = markUpdateTimeFromFiberToRoot(boundaryFiber, retryTime);1607 if (root !== null) {1608 scheduleCallbackForRoot(root, priorityLevel, retryTime);1609 }1610}1611export function resolveRetryThenable(boundaryFiber: Fiber, thenable: Thenable) {1612 let retryCache: WeakSet<Thenable> | Set<Thenable> | null;1613 if (enableSuspenseServerRenderer) {1614 switch (boundaryFiber.tag) {1615 case SuspenseComponent:1616 retryCache = boundaryFiber.stateNode;1617 break;1618 case DehydratedSuspenseComponent:1619 retryCache = boundaryFiber.memoizedState;1620 break;1621 default:1622 invariant(1623 false,1624 'Pinged unknown suspense boundary type. ' +1625 'This is probably a bug in React.',1626 );1627 }1628 } else {1629 retryCache = boundaryFiber.stateNode;1630 }1631 if (retryCache !== null) {1632 // The thenable resolved, so we no longer need to memoize, because it will1633 // never be thrown again.1634 retryCache.delete(thenable);1635 }1636 retryTimedOutBoundary(boundaryFiber);1637}1638export function inferStartTimeFromExpirationTime(1639 root: FiberRoot,1640 expirationTime: ExpirationTime,1641) {1642 // We don't know exactly when the update was scheduled, but we can infer an1643 // approximate start time from the expiration time.1644 const earliestExpirationTimeMs = expirationTimeToMs(root.firstPendingTime);1645 // TODO: Track this on the root instead. It's more accurate, doesn't rely on1646 // assumptions about priority, and isn't coupled to Scheduler details.1647 return earliestExpirationTimeMs - LOW_PRIORITY_EXPIRATION;1648}1649function computeMsUntilTimeout(root, absoluteTimeoutMs) {1650 if (disableYielding) {1651 // Timeout immediately when yielding is disabled.1652 return 0;1653 }1654 // Find the earliest uncommitted expiration time in the tree, including1655 // work that is suspended. The timeout threshold cannot be longer than1656 // the overall expiration.1657 const earliestExpirationTimeMs = expirationTimeToMs(root.firstPendingTime);1658 if (earliestExpirationTimeMs < absoluteTimeoutMs) {1659 absoluteTimeoutMs = earliestExpirationTimeMs;1660 }1661 // Subtract the current time from the absolute timeout to get the number1662 // of milliseconds until the timeout. In other words, convert an absolute1663 // timestamp to a relative time. This is the value that is passed1664 // to `setTimeout`.1665 let msUntilTimeout = absoluteTimeoutMs - now();1666 return msUntilTimeout < 0 ? 0 : msUntilTimeout;1667}1668function checkForNestedUpdates() {1669 if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {1670 nestedUpdateCount = 0;1671 rootWithNestedUpdates = null;1672 invariant(1673 false,1674 'Maximum update depth exceeded. This can happen when a component ' +1675 'repeatedly calls setState inside componentWillUpdate or ' +1676 'componentDidUpdate. React limits the number of nested updates to ' +1677 'prevent infinite loops.',1678 );1679 }1680 if (__DEV__) {1681 if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {1682 nestedPassiveUpdateCount = 0;1683 warning(1684 false,1685 'Maximum update depth exceeded. This can happen when a component ' +1686 "calls setState inside useEffect, but useEffect either doesn't " +1687 'have a dependency array, or one of the dependencies changes on ' +1688 'every render.',1689 );1690 }1691 }1692}1693function flushRenderPhaseStrictModeWarningsInDEV() {1694 if (__DEV__) {1695 ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();1696 ReactStrictModeWarnings.flushLegacyContextWarning();1697 if (warnAboutDeprecatedLifecycles) {1698 ReactStrictModeWarnings.flushPendingDeprecationWarnings();1699 }1700 }1701}1702function stopFinishedWorkLoopTimer() {1703 const didCompleteRoot = true;1704 stopWorkLoopTimer(interruptedBy, didCompleteRoot);1705 interruptedBy = null;1706}1707function stopInterruptedWorkLoopTimer() {1708 // TODO: Track which fiber caused the interruption.1709 const didCompleteRoot = false;1710 stopWorkLoopTimer(interruptedBy, didCompleteRoot);1711 interruptedBy = null;1712}1713function checkForInterruption(1714 fiberThatReceivedUpdate: Fiber,1715 updateExpirationTime: ExpirationTime,1716) {1717 if (1718 enableUserTimingAPI &&1719 workInProgressRoot !== null &&1720 updateExpirationTime > renderExpirationTime1721 ) {1722 interruptedBy = fiberThatReceivedUpdate;1723 }1724}1725let didWarnStateUpdateForUnmountedComponent: Set<string> | null = null;1726function warnAboutUpdateOnUnmountedFiberInDEV(fiber) {1727 if (__DEV__) {1728 const tag = fiber.tag;1729 if (1730 tag !== HostRoot &&1731 tag !== ClassComponent &&1732 tag !== FunctionComponent &&1733 tag !== ForwardRef &&1734 tag !== MemoComponent &&1735 tag !== SimpleMemoComponent1736 ) {1737 // Only warn for user-defined components, not internal ones like Suspense.1738 return;1739 }1740 // We show the whole stack but dedupe on the top component's name because...

Full Screen

Full Screen

ReactFiberWorkLoop.js

Source:ReactFiberWorkLoop.js Github

copy

Full Screen

...215 // checkForNestedUpdates()216 // warnAboutRenderPhaseUpdatesInDEV(fiber);217 const root = markUpdateLaneFromFiberToRoot(fiber, lane);218 if (root === null) {219 // warnAboutUpdateOnUnmountedFiberInDEV(fiber);220 return null;221 }222 // Mark that the root has a pending update.223 markRootUpdated(root, lane, eventTime);224 // if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {225 // // ...226 // }227 if (root === workInProgressRoot) {228 // Received an update to a tree that's in the middle of rendering. Mark229 // that there was an interleaved update work on this root. Unless the230 // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render231 // phase update. In that case, we don't treat render phase updates as if232 // they were interleaved, for backwards compat reasons.233 if (...

Full Screen

Full Screen

scheduleUpdateOnFiber.js

Source:scheduleUpdateOnFiber.js Github

copy

Full Screen

2 checkForNestedUpdates();3 warnAboutInvalidUpdatesOnClassComponentsInDEV(fiber);4 var root = markUpdateTimeFromFiberToRoot(fiber, expirationTime);5 if (root === null) {6 warnAboutUpdateOnUnmountedFiberInDEV(fiber);7 return;8 }9 checkForInterruption(fiber, expirationTime);10 recordScheduleUpdate(); // TODO: computeExpirationForFiber also reads the priority. Pass the11 // priority as an argument to that function and this one.12 var priorityLevel = getCurrentPriorityLevel();13 if (expirationTime === Sync) {14 if ( // Check if we're inside unbatchedUpdates15 (executionContext & LegacyUnbatchedContext) !== NoContext && // Check if we're not already rendering16 (executionContext & (RenderContext | CommitContext)) === NoContext) {17 // Register pending interactions on the root to avoid losing traced interaction data.18 schedulePendingInteractions(root, expirationTime); // This is a legacy edge case. The initial mount of a ReactDOM.render-ed19 // root inside of batchedUpdates should be synchronous, but layout updates20 // should be deferred until the end of the batch....

Full Screen

Full Screen

1-2__scheduleUpdateOnFiber.js

Source:1-2__scheduleUpdateOnFiber.js Github

copy

Full Screen

2 checkForNestedUpdates();3 warnAboutInvalidUpdatesOnClassComponentsInDEV(fiber);4 var root = markUpdateTimeFromFiberToRoot(fiber, expirationTime);5 if (root === null) {6 warnAboutUpdateOnUnmountedFiberInDEV(fiber);7 return;8 }9 checkForInterruption(fiber, expirationTime);10 recordScheduleUpdate(); // TODO: computeExpirationForFiber also reads the priority. Pass the11 // priority as an argument to that function and this one.12 var priorityLevel = getCurrentPriorityLevel();13 if (expirationTime === Sync) {14 if ( // Check if we're inside unbatchedUpdates15 (executionContext & LegacyUnbatchedContext) !== NoContext && // Check if we're not already rendering16 (executionContext & (RenderContext | CommitContext)) === NoContext) {17 // Register pending interactions on the root to avoid losing traced interaction data.18 schedulePendingInteractions(root, expirationTime); // This is a legacy edge case. The initial mount of a ReactDOM.render-ed19 // root inside of batchedUpdates should be synchronous, but layout updates20 // should be deferred until the end of the batch....

Full Screen

Full Screen

updateEqueue.js

Source:updateEqueue.js Github

copy

Full Screen

...49) {50 checkForNestedUpdates();51 const root = markUpdateTimeFromFiberToRoot(fiber, expirationTime);52 if (root === null) {53 warnAboutUpdateOnUnmountedFiberInDEV(fiber);54 return;55 }56 checkForInterruption(fiber, expirationTime);57 const priorityLevel = getCurrentPriorityLevel();58 if (expirationTime === Sync) {59 if (60 (executionContext & LegacyUnbatchedContext) !== NoContext &&61 (executionContext & (RenderContext | CommitContext)) === NoContext62 ) {63 schedulePendingInteractions(root, expirationTime);64 performSyncWorkOnRoot(root);65 } else {66 ensureRootIsScheduled(root);67 schedulePendingInteractions(root, expirationTime);...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1const { chromium } = require('playwright');2const { warnAboutUpdateOnUnmountedFiberInDEV } = require('react-dom/cjs/react-dom.development');3(async () => {4 const browser = await chromium.launch();5 const context = await browser.newContext();6 const page = await context.newPage();7 await page.click('text=Counter');8 await page.click('text=Toggle');9 await page.click('text=Toggle');10 const result = warnAboutUpdateOnUnmountedFiberInDEV('Counter');11 console.log(result);12 await browser.close();13})();14 throw new Error(`Request Interception is not enabled!`);15await page.route('**/*', route => {16 route.continue();17});18await page.route('**/*', route => {19 route.continue();20});21await page.route('**/*', route => {22 route.continue();23});24await page.route('**/*', route => {25 route.continue();26});27await page.route('**/*', route => {28 route.continue();29});30Your name to display (optional):31Your name to display (optional):32await page.route('**/*', route => {33 route.continue();34});35Your name to display (optional):36Your name to display (optional):37await page.route('**/*', route => {38 route.continue();39});

Full Screen

Using AI Code Generation

copy

Full Screen

1const { warnAboutUpdateOnUnmountedFiberInDEV } = require('playwright/lib/internal');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 page.click('text=Get Started');8 await page.click('text=Docs');9 await page.click('text=API');10 await page.click('text=class: Page');11 await page.click('text=method: Page.click');12 await page.click('text=Examples');13 await page.click('text=Click a button');14 await page.click('text=Run');15 await page.click('text=Close');16 await page.click('text=class: Page');17 await page.click('text=method: Page.click');18 await page.click('text=Examples');19 await page.click('text=Click a button');20 await page.click('text=Run');21 await page.click('text=Close');22 await page.click('text=class: Page');23 await page.click('text=method: Page.click');24 await page.click('text=Examples');25 await page.click('text=Click a button');26 await page.click('text=Run');27 await page.click('text=Close');28 await page.click('text=class: Page');29 await page.click('text=method: Page.click');30 await page.click('text=Examples');31 await page.click('text=Click a button');32 await page.click('text=Run');33 await page.click('text=Close');34 await page.click('text=class: Page');35 await page.click('text=method: Page.click');36 await page.click('text=Examples');37 await page.click('text=Click a button');38 await page.click('text=Run');39 await page.click('text=Close');40 await page.click('text=class: Page');41 await page.click('text=method: Page.click');42 await page.click('text=Examples');43 await page.click('text=Click a button');44 await page.click('text=Run');45 await page.click('text=Close');46 await page.click('text=class: Page');47 await page.click('text=method: Page.click');48 await page.click('text=Examples

Full Screen

Using AI Code Generation

copy

Full Screen

1const { warnAboutUpdateOnUnmountedFiberInDEV } = require('@playwright/test/lib/server/inspector/inspector');2const { test } = require('@playwright/test');3test('should warn about update on unmounted Fiber', async ({ page }) => {4 await page.evaluate(() => {5 warnAboutUpdateOnUnmountedFiberInDEV();6 });7});8const { test } = require('@playwright/test');9test('should log all messages sent from the extension to the browser console', async ({ page }) => {10 await page.context().addInitScript(() => {11 window.addEventListener('message', (event) => {12 if (event.source === window && event.data && event.data.source === 'react-devtools-content-script') {13 console.log('Received message from the extension:', event.data);14 }15 });16 });17});18Received message from the extension: {source: "react-devtools-content-script", type: "ping"}19const { test } = require('@playwright/test');20test('should send the "ping" message to the extension', async

Full Screen

Using AI Code Generation

copy

Full Screen

1const { warnAboutUpdateOnUnmountedFiberInDEV } = require('@playwright/test/lib/server/trace/recorder/recorder');2warnAboutUpdateOnUnmountedFiberInDEV('test');3const { warnAboutUpdateOnUnmountedFiberInDEV } = require('@playwright/test/lib/server/trace/recorder/recorder');4warnAboutUpdateOnUnmountedFiberInDEV('test');5const { warnAboutUpdateOnUnmountedFiberInDEV } = require('@playwright/test/lib/server/trace/recorder/recorder');6warnAboutUpdateOnUnmountedFiberInDEV('test');7const { warnAboutUpdateOnUnmountedFiberInDEV } = require('@playwright/test/lib/server/trace/recorder/recorder');8warnAboutUpdateOnUnmountedFiberInDEV('test');9const { warnAboutUpdateOnUnmountedFiberInDEV } = require('@playwright/test/lib/server/trace/recorder/recorder');10warnAboutUpdateOnUnmountedFiberInDEV('test');11const { warnAboutUpdateOnUnmountedFiberInDEV } = require('@playwright/test/lib/server/trace/recorder/recorder');12warnAboutUpdateOnUnmountedFiberInDEV('test');

Full Screen

Using AI Code Generation

copy

Full Screen

1const { warnAboutUpdateOnUnmountedFiberInDEV } = require( 'react-dom/cjs/react-dom.development' );2warnAboutUpdateOnUnmountedFiberInDEV( 'test' );3module.exports = {4 use: {5 viewport: { width: 1200, height: 1000 },6 },7 {8 use: {9 },10 },11};12 at reportException (node_modules/playwright-core/lib/cjs/pw-runner/workerRunner.js:262:15)13 at runNextTicks (node_modules/process/next_tick.js:52:3)14 at processImmediate (node_modules/process/next_tick.js:63:5)15 at warnAboutUpdateOnUnmountedFiberInDEV (node_modules/react-dom/cjs/react-dom.development.js:23269:19)16 at scheduleUpdateOnFiber (node_modules/react-dom/cjs/react-dom.development.js:21261:7)17 at Object.enqueueSetState (node_modules/react-dom/cjs/react-dom.development.js:12934:5)18 at test.setState (node_modules/react/cjs/react.development.js:325:16)19 at warnAboutUpdateOnUnmountedFiberInDEV (node_modules/react-dom/cjs/react-dom.development.js:23269:19)20 at scheduleUpdateOnFiber (node_modules/react-dom/cjs/react-dom.development.js:21261:7)21 at Object.enqueueSetState (node_modules/react-dom/cjs/react-dom.development.js:12934:

Full Screen

Using AI Code Generation

copy

Full Screen

1const { warnAboutUpdateOnUnmountedFiberInDEV } = require('playwright/lib/server/webkit/wk');2warnAboutUpdateOnUnmountedFiberInDEV();3const { warnAboutStringRefsInDEV } = require('playwright/lib/server/webkit/wk');4warnAboutStringRefsInDEV();5const { warnAboutDeprecatedLifecycles } = require('playwright/lib/server/webkit/wk');6warnAboutDeprecatedLifecycles();7const { warnAboutDefaultPropsOnFunctionComponents } = require('playwright/lib/server/webkit/wk');8warnAboutDefaultPropsOnFunctionComponents();9const { warnAboutDirectlyAssigningPropsToState } = require('playwright/lib/server/webkit/wk');10warnAboutDirectlyAssigningPropsToState();11const { warnAboutContextTypeOnFunctionComponent } = require('playwright/lib/server/webkit/wk');12warnAboutContextTypeOnFunctionComponent();13const { warnAboutDeprecatedFindDOMNode } = require('playwright/lib/server/webkit/wk');14warnAboutDeprecatedFindDOMNode();15const { warnAboutShorthandPropertyCollision } = require('playwright/lib/server/webkit/wk');16warnAboutShorthandPropertyCollision();17const { warnAboutFunctionRefs } = require('playwright/lib/server/webkit/wk');18warnAboutFunctionRefs();19const { warnAboutReassigningProps } = require('playwright/lib/server/webkit/wk');

Full Screen

Using AI Code Generation

copy

Full Screen

1const { warnAboutUpdateOnUnmountedFiberInDEV } = require('playwright/lib/server/trace/recorder/recorderTraceEvents');2warnAboutUpdateOnUnmountedFiberInDEV();3import { test } from '@playwright/test';4test('example test', async ({ page }) => {5});6import { test } from '@playwright/test';7test('example test', async ({ page }) => {8});9import { test } from '@playwright/test';10test('example test', async ({ page }) => {11});12import { test } from '@playwright/test';13test('example test', async ({ page }) => {14});15import { test } from '@playwright/test';16test('example test', async ({ page }) => {17});18import { test } from '@playwright/test';19test('example test', async ({ page }) => {20});21import { test } from '@playwright/test';22test('example test', async ({ page }) => {23});24import { test } from '@playwright/test';25test('example test', async ({ page }) => {26});27import { test } from '@playwright/test';28test('example test', async ({ page }) => {29});

Full Screen

Playwright tutorial

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.

Chapters:

  1. What is Playwright : Playwright is comparatively new but has gained good popularity. Get to know some history of the Playwright with some interesting facts connected with it.
  2. How To Install Playwright : Learn in detail about what basic configuration and dependencies are required for installing Playwright and run a test. Get a step-by-step direction for installing the Playwright automation framework.
  3. Playwright Futuristic Features: Launched in 2020, Playwright gained huge popularity quickly because of some obliging features such as Playwright Test Generator and Inspector, Playwright Reporter, Playwright auto-waiting mechanism and etc. Read up on those features to master Playwright testing.
  4. What is Component Testing: Component testing in Playwright is a unique feature that allows a tester to test a single component of a web application without integrating them with other elements. Learn how to perform Component testing on the Playwright automation framework.
  5. Inputs And Buttons In Playwright: Every website has Input boxes and buttons; learn about testing inputs and buttons with different scenarios and examples.
  6. Functions and Selectors in Playwright: Learn how to launch the Chromium browser with Playwright. Also, gain a better understanding of some important functions like “BrowserContext,” which allows you to run multiple browser sessions, and “newPage” which interacts with a page.
  7. Handling Alerts and Dropdowns in Playwright : Playwright interact with different types of alerts and pop-ups, such as simple, confirmation, and prompt, and different types of dropdowns, such as single selector and multi-selector get your hands-on with handling alerts and dropdown in Playright testing.
  8. Playwright vs Puppeteer: Get to know about the difference between two testing frameworks and how they are different than one another, which browsers they support, and what features they provide.
  9. Run Playwright Tests on LambdaTest: Playwright testing with LambdaTest leverages test performance to the utmost. You can run multiple Playwright tests in Parallel with the LammbdaTest test cloud. Get a step-by-step guide to run your Playwright test on the LambdaTest platform.
  10. Playwright Python Tutorial: Playwright automation framework support all major languages such as Python, JavaScript, TypeScript, .NET and etc. However, there are various advantages to Python end-to-end testing with Playwright because of its versatile utility. Get the hang of Playwright python testing with this chapter.
  11. Playwright End To End Testing Tutorial: Get your hands on with Playwright end-to-end testing and learn to use some exciting features such as TraceViewer, Debugging, Networking, Component testing, Visual testing, and many more.
  12. Playwright Video Tutorial: Watch the video tutorials on Playwright testing from experts and get a consecutive in-depth explanation of Playwright automation testing.

Run Playwright Internal automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful