Best JavaScript code snippet using playwright-internal
ReactFiberWorkLoop.new.js
Source:ReactFiberWorkLoop.new.js
...685 //686 // So we'll throw out the current work and restart.687 // å¦æå¨renderè¿ç¨ä¸äº§çäºæ°çupdate, ä¸æ°updateçä¼å
级ä¸æårenderçä¼å
级æ交é688 // é£ä¹æårenderæ æ, 丢å¼æårenderçç»æ, çå¾
ä¸ä¸æ¬¡è°åº¦689 prepareFreshStack(root, NoLanes);690 } else if (exitStatus !== RootIncomplete) {691 // 4. å¼å¸¸å¤ç: æå¯è½fiberæé è¿ç¨ä¸åºç°å¼å¸¸692 if (exitStatus === RootErrored) {693 executionContext |= RetryAfterError;694 // If an error occurred during hydration,695 // discard server response and fall back to client side render.696 if (root.hydrate) {697 root.hydrate = false;698 clearContainer(root.containerInfo);699 }700 // If something threw an error, try rendering one more time. We'll render701 // synchronously to block concurrent data mutations, and we'll includes702 // all pending updates are included. If it still fails after the second703 // attempt, we'll give up and commit the resulting tree.704 lanes = getLanesToRetrySynchronouslyOnError(root);705 if (lanes !== NoLanes) {706 exitStatus = renderRootSync(root, lanes);707 }708 }709 if (exitStatus === RootFatalErrored) {710 const fatalError = workInProgressRootFatalError;711 prepareFreshStack(root, NoLanes);712 markRootSuspended(root, lanes);713 ensureRootIsScheduled(root, now());714 throw fatalError;715 }716 // We now have a consistent tree. The next step is either to commit it,717 // or, if something suspended, wait to commit it after a timeout.718 const finishedWork: Fiber = (root.current.alternate: any);719 root.finishedWork = finishedWork;720 root.finishedLanes = lanes;721 // 5. è¾åº: 渲æfiberæ 722 finishConcurrentRender(root, exitStatus, lanes);723 }724 // è°ç¨ensureRootIsScheduledå»æ£æ¥ææ è¿æä»»å¡ï¼æ¯å¦éè¦è°åº¦è¿æä»»å¡725 ensureRootIsScheduled(root, now());726 // æ´æ°ä»»å¡æªå®æï¼returnèªå·±ï¼æ¹ä¾¿Schedulerå¤æä»»å¡å®æç¶æ727 if (root.callbackNode === originalCallbackNode) {728 // The task node scheduled for this root is the same one that's729 // currently executed. Need to return a continuation.730 return performConcurrentWorkOnRoot.bind(null, root);731 }732 enableLog && console.log('performConcurrentWorkOnRoot end')733 // å¦åretutn nullï¼è¡¨ç¤ºä»»å¡å·²ç»å®æï¼éç¥Scheduleråæ¢è°åº¦734 return null;735}736function finishConcurrentRender(root, exitStatus, lanes) {737 switch (exitStatus) {738 case RootIncomplete:739 case RootFatalErrored: {740 invariant(false, 'Root did not complete. This is a bug in React.');741 }742 // Flow knows about invariant, so it complains if I add a break743 // statement, but eslint doesn't know about invariant, so it complains744 // if I do. eslint-disable-next-line no-fallthrough745 case RootErrored: {746 // We should have already attempted to retry this tree. If we reached747 // this point, it errored again. Commit it.748 commitRoot(root);749 break;750 }751 case RootSuspended: {752 markRootSuspended(root, lanes);753 // We have an acceptable loading state. We need to figure out if we754 // should immediately commit it or wait a bit.755 if (756 includesOnlyRetries(lanes)757 ) {758 // This render only included retries, no updates. Throttle committing759 // retries so that we don't show too many loading states too quickly.760 const msUntilTimeout =761 globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now();762 // Don't bother with a very short suspense time.763 if (msUntilTimeout > 10) {764 const nextLanes = getNextLanes(root, NoLanes);765 if (nextLanes !== NoLanes) {766 // There's additional work on this root.767 break;768 }769 const suspendedLanes = root.suspendedLanes;770 if (!isSubsetOfLanes(suspendedLanes, lanes)) {771 // We should prefer to render the fallback of at the last772 // suspended level. Ping the last suspended level to try773 // rendering it again.774 // FIXME: What if the suspended lanes are Idle? Should not restart.775 const eventTime = requestEventTime();776 markRootPinged(root, suspendedLanes, eventTime);777 break;778 }779 // The render is suspended, it hasn't timed out, and there's no780 // lower priority work to do. Instead of committing the fallback781 // immediately, wait for more data to arrive.782 root.timeoutHandle = scheduleTimeout(783 commitRoot.bind(null, root),784 msUntilTimeout,785 );786 break;787 }788 }789 // The work expired. Commit immediately.790 commitRoot(root);791 break;792 }793 case RootSuspendedWithDelay: {794 markRootSuspended(root, lanes);795 if (includesOnlyTransitions(lanes)) {796 // This is a transition, so we should exit without committing a797 // placeholder and without scheduling a timeout. Delay indefinitely798 // until we receive more data.799 break;800 }801 // This is not a transition, but we did trigger an avoided state.802 // Schedule a placeholder to display after a short delay, using the Just803 // Noticeable Difference.804 // TODO: Is the JND optimization worth the added complexity? If this is805 // the only reason we track the event time, then probably not.806 // Consider removing.807 const mostRecentEventTime = getMostRecentEventTime(root, lanes);808 const eventTimeMs = mostRecentEventTime;809 const timeElapsedMs = now() - eventTimeMs;810 const msUntilTimeout = jnd(timeElapsedMs) - timeElapsedMs;811 // Don't bother with a very short suspense time.812 if (msUntilTimeout > 10) {813 // Instead of committing the fallback immediately, wait for more data814 // to arrive.815 root.timeoutHandle = scheduleTimeout(816 commitRoot.bind(null, root),817 msUntilTimeout,818 );819 break;820 }821 // Commit the placeholder.822 commitRoot(root);823 break;824 }825 case RootCompleted: {826 // The work completed. Ready to commit.827 commitRoot(root);828 break;829 }830 default: {831 invariant(false, 'Unknown root exit status.');832 }833 }834}835function markRootSuspended(root, suspendedLanes) {836 // When suspending, we should always exclude lanes that were pinged or (more837 // rarely, since we try to avoid it) updated during the render phase.838 // TODO: Lol maybe there's a better way to factor this besides this839 // obnoxiously named function :)840 suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);841 suspendedLanes = removeLanes(suspendedLanes, workInProgressRootUpdatedLanes);842 markRootSuspended_dontCallThisOneDirectly(root, suspendedLanes);843}844// This is the entry point for synchronous tasks that don't go845// through Scheduler846function performSyncWorkOnRoot(root) {847 invariant(848 (executionContext & (RenderContext | CommitContext)) === NoContext,849 'Should not already be working.',850 );851 852 console.log('ReactFiberWorkLoop.new: performSyncWorkOnRoot')853 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('performSyncWorkOnRoot')) debugger854 flushPassiveEffects();855 let lanes;856 let exitStatus;857 if (858 root === workInProgressRoot &&859 includesSomeLane(root.expiredLanes, workInProgressRootRenderLanes)860 ) {861 // There's a partial tree, and at least one of its lanes has expired. Finish862 // rendering it before rendering the rest of the expired work.863 lanes = workInProgressRootRenderLanes;864 exitStatus = renderRootSync(root, lanes);865 if (866 includesSomeLane(867 workInProgressRootIncludedLanes,868 workInProgressRootUpdatedLanes,869 )870 ) {871 // The render included lanes that were updated during the render phase.872 // For example, when unhiding a hidden tree, we include all the lanes873 // that were previously skipped when the tree was hidden. That set of874 // lanes is a superset of the lanes we started rendering with.875 //876 // Note that this only happens when part of the tree is rendered877 // concurrently. If the whole tree is rendered synchronously, then there878 // are no interleaved events.879 lanes = getNextLanes(root, lanes);880 exitStatus = renderRootSync(root, lanes);881 }882 } else {883 lanes = getNextLanes(root, NoLanes);884 exitStatus = renderRootSync(root, lanes);885 }886 if (root.tag !== LegacyRoot && exitStatus === RootErrored) {887 executionContext |= RetryAfterError;888 // If an error occurred during hydration,889 // discard server response and fall back to client side render.890 if (root.hydrate) {891 root.hydrate = false;892 clearContainer(root.containerInfo);893 }894 // If something threw an error, try rendering one more time. We'll render895 // synchronously to block concurrent data mutations, and we'll includes896 // all pending updates are included. If it still fails after the second897 // attempt, we'll give up and commit the resulting tree.898 lanes = getLanesToRetrySynchronouslyOnError(root);899 if (lanes !== NoLanes) {900 exitStatus = renderRootSync(root, lanes);901 }902 }903 if (exitStatus === RootFatalErrored) {904 const fatalError = workInProgressRootFatalError;905 prepareFreshStack(root, NoLanes);906 markRootSuspended(root, lanes);907 ensureRootIsScheduled(root, now());908 throw fatalError;909 }910 // We now have a consistent tree. Because this is a sync render, we911 // will commit it even if something suspended.912 const finishedWork: Fiber = (root.current.alternate: any);913 root.finishedWork = finishedWork;914 root.finishedLanes = lanes;915 commitRoot(root);916 // Before exiting, make sure there's a callback scheduled for the next917 // pending level.918 ensureRootIsScheduled(root, now());919 return null;920}921export function flushRoot(root: FiberRoot, lanes: Lanes) {922 markRootExpired(root, lanes);923 ensureRootIsScheduled(root, now());924 if ((executionContext & (RenderContext | CommitContext)) === NoContext) {925 resetRenderTimer();926 flushSyncCallbackQueue();927 }928}929export function getExecutionContext(): ExecutionContext {930 return executionContext;931}932export function flushDiscreteUpdates() {933 // TODO: Should be able to flush inside batchedUpdates, but not inside `act`.934 // However, `act` uses `batchedUpdates`, so there's no way to distinguish935 // those two cases. Need to fix this before exposing flushDiscreteUpdates936 // as a public API.937 if (938 (executionContext & (BatchedContext | RenderContext | CommitContext)) !==939 NoContext940 ) {941 // We're already rendering, so we can't synchronously flush pending work.942 // This is probably a nested event dispatch triggered by a lifecycle/effect,943 // like `el.focus()`. Exit.944 return;945 }946 flushPendingDiscreteUpdates();947 // If the discrete updates scheduled passive effects, flush them now so that948 // they fire before the next serial event.949 flushPassiveEffects();950}951export function deferredUpdates<A>(fn: () => A): A {952 return runWithPriority(NormalSchedulerPriority, fn);953}954function flushPendingDiscreteUpdates() {955 if (rootsWithPendingDiscreteUpdates !== null) {956 // For each root with pending discrete updates, schedule a callback to957 // immediately flush them.958 const roots = rootsWithPendingDiscreteUpdates;959 rootsWithPendingDiscreteUpdates = null;960 roots.forEach(root => {961 markDiscreteUpdatesExpired(root);962 ensureRootIsScheduled(root, now());963 });964 }965 // Now flush the immediate queue.966 flushSyncCallbackQueue();967}968export function batchedUpdates<A, R>(fn: A => R, a: A): R {969 const prevExecutionContext = executionContext;970 executionContext |= BatchedContext;971 try {972 return fn(a);973 } finally {974 executionContext = prevExecutionContext;975 if (executionContext === NoContext) {976 // Flush the immediate callbacks that were scheduled during this batch977 resetRenderTimer();978 flushSyncCallbackQueue();979 }980 }981}982export function batchedEventUpdates<A, R>(fn: A => R, a: A): R {983 const prevExecutionContext = executionContext;984 // ææçäºä»¶å¨è§¦åçæ¶åï¼é½ä¼å
è°ç¨ batchedEventUpdates è¿ä¸ªæ¹æ³985 // å¨è¿éå°±ä¼ä¿®æ¹ executionContext çå¼ï¼React å°±ç¥éæ¤æ¶ç setState å¨èªå·±çææ§ä¸986 executionContext |= EventContext;987 try {988 return fn(a);989 } finally {990 executionContext = prevExecutionContext;991 // è°ç¨ç»æåï¼è°ç¨ flushSyncCallbackQueue992 if (executionContext === NoContext) {993 // Flush the immediate callbacks that were scheduled during this batch994 resetRenderTimer();995 flushSyncCallbackQueue();996 }997 }998}999export function discreteUpdates<A, B, C, D, R>(1000 fn: (A, B, C) => R,1001 a: A,1002 b: B,1003 c: C,1004 d: D,1005): R {1006 const prevExecutionContext = executionContext;1007 executionContext |= DiscreteEventContext;1008 try {1009 return runWithPriority(1010 UserBlockingSchedulerPriority,1011 fn.bind(null, a, b, c, d),1012 );1013 } finally {1014 executionContext = prevExecutionContext;1015 if (executionContext === NoContext) {1016 // Flush the immediate callbacks that were scheduled during this batch1017 resetRenderTimer();1018 flushSyncCallbackQueue();1019 }1020 }1021}1022export function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {1023 const prevExecutionContext = executionContext;1024 executionContext &= ~BatchedContext;1025 executionContext |= LegacyUnbatchedContext;1026 try {1027 return fn(a);1028 } finally {1029 executionContext = prevExecutionContext;1030 if (executionContext === NoContext) {1031 // Flush the immediate callbacks that were scheduled during this batch1032 resetRenderTimer();1033 flushSyncCallbackQueue();1034 }1035 }1036}1037export function flushSync<A, R>(fn: A => R, a: A): R {1038 const prevExecutionContext = executionContext;1039 if ((prevExecutionContext & (RenderContext | CommitContext)) !== NoContext) {1040 return fn(a);1041 }1042 executionContext |= BatchedContext;1043 try {1044 if (fn) {1045 return runWithPriority(ImmediateSchedulerPriority, fn.bind(null, a));1046 } else {1047 return (undefined: $FlowFixMe);1048 }1049 } finally {1050 executionContext = prevExecutionContext;1051 // Flush the immediate callbacks that were scheduled during this batch.1052 // Note that this will happen even if batchedUpdates is higher up1053 // the stack.1054 flushSyncCallbackQueue();1055 }1056}1057export function flushControlled(fn: () => mixed): void {1058 const prevExecutionContext = executionContext;1059 executionContext |= BatchedContext;1060 try {1061 runWithPriority(ImmediateSchedulerPriority, fn);1062 } finally {1063 executionContext = prevExecutionContext;1064 if (executionContext === NoContext) {1065 // Flush the immediate callbacks that were scheduled during this batch1066 resetRenderTimer();1067 flushSyncCallbackQueue();1068 }1069 }1070}1071export function pushRenderLanes(fiber: Fiber, lanes: Lanes) {1072 pushToStack(subtreeRenderLanesCursor, subtreeRenderLanes, fiber);1073 subtreeRenderLanes = mergeLanes(subtreeRenderLanes, lanes);1074 workInProgressRootIncludedLanes = mergeLanes(1075 workInProgressRootIncludedLanes,1076 lanes,1077 );1078}1079export function popRenderLanes(fiber: Fiber) {1080 subtreeRenderLanes = subtreeRenderLanesCursor.current;1081 popFromStack(subtreeRenderLanesCursor, fiber);1082}1083/**1084å·æ°æ 帧: éç½® FiberRootä¸çå
¨å±å±æ§ å `fiberæ æé `循ç¯è¿ç¨ä¸çå
¨å±åé1085*/1086function prepareFreshStack(root: FiberRoot, lanes: Lanes) {1087 // workInProgressRoot第ä¸æ¬¡å¨è¿éåå§å1088 enableLog && console.log('prepareFreshStack start')1089 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('prepareFreshStack')) debugger1090 1091 root.finishedWork = null;1092 root.finishedLanes = NoLanes;1093 const timeoutHandle = root.timeoutHandle;1094 if (timeoutHandle !== noTimeout) { // noTimeout === -11095 // The root previous suspended and scheduled a timeout to commit a fallback1096 // state. Now that we have additional work, cancel the timeout.1097 root.timeoutHandle = noTimeout;1098 // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above1099 cancelTimeout(timeoutHandle);1100 }1101 if (workInProgress !== null) {1102 let interruptedWork = workInProgress.return;1103 while (interruptedWork !== null) {1104 unwindInterruptedWork(interruptedWork);1105 interruptedWork = interruptedWork.return;1106 }1107 }1108 workInProgressRoot = root;1109 workInProgress = createWorkInProgress(root.current, null);1110 workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;1111 workInProgressRootExitStatus = RootIncomplete;1112 workInProgressRootFatalError = null;1113 workInProgressRootSkippedLanes = NoLanes;1114 workInProgressRootUpdatedLanes = NoLanes;1115 workInProgressRootPingedLanes = NoLanes;1116 if (enableSchedulerTracing) {1117 spawnedWorkDuringRender = null;1118 }1119 enableLog && console.log('prepareFreshStack end')1120}1121function handleError(root, thrownValue): void {1122 do {1123 let erroredWork = workInProgress;1124 try {1125 // Reset module-level state that was set during the render phase.1126 resetContextDependencies();1127 resetHooksAfterThrow();1128 // TODO: I found and added this missing line while investigating a1129 // separate issue. Write a regression test using string refs.1130 ReactCurrentOwner.current = null;1131 if (erroredWork === null || erroredWork.return === null) {1132 // Expected to be working on a non-root fiber. This is a fatal error1133 // because there's no ancestor that can handle it; the root is1134 // supposed to capture all errors that weren't caught by an error1135 // boundary.1136 workInProgressRootExitStatus = RootFatalErrored;1137 workInProgressRootFatalError = thrownValue;1138 // Set `workInProgress` to null. This represents advancing to the next1139 // sibling, or the parent if there are no siblings. But since the root1140 // has no siblings nor a parent, we set it to null. Usually this is1141 // handled by `completeUnitOfWork` or `unwindWork`, but since we're1142 // intentionally not calling those, we need set it here.1143 // TODO: Consider calling `unwindWork` to pop the contexts.1144 workInProgress = null;1145 return;1146 }1147 if (enableProfilerTimer && erroredWork.mode & ProfileMode) {1148 // Record the time spent rendering before an error was thrown. This1149 // avoids inaccurate Profiler durations in the case of a1150 // suspended render.1151 stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true);1152 }1153 throwException(1154 root,1155 erroredWork.return,1156 erroredWork,1157 thrownValue,1158 workInProgressRootRenderLanes,1159 );1160 completeUnitOfWork(erroredWork);1161 } catch (yetAnotherThrownValue) {1162 // Something in the return path also threw.1163 thrownValue = yetAnotherThrownValue;1164 if (workInProgress === erroredWork && erroredWork !== null) {1165 // If this boundary has already errored, then we had trouble processing1166 // the error. Bubble it to the next boundary.1167 erroredWork = erroredWork.return;1168 workInProgress = erroredWork;1169 } else {1170 erroredWork = workInProgress;1171 }1172 continue;1173 }1174 // Return to the normal work loop.1175 return;1176 } while (true);1177}1178function pushDispatcher() {1179 const prevDispatcher = ReactCurrentDispatcher.current;1180 ReactCurrentDispatcher.current = ContextOnlyDispatcher;1181 if (prevDispatcher === null) {1182 // The React isomorphic package does not include a default dispatcher.1183 // Instead the first renderer will lazily attach one, in order to give1184 // nicer error messages.1185 return ContextOnlyDispatcher;1186 } else {1187 return prevDispatcher;1188 }1189}1190function popDispatcher(prevDispatcher) {1191 ReactCurrentDispatcher.current = prevDispatcher;1192}1193function pushInteractions(root) {1194 if (enableSchedulerTracing) {1195 const prevInteractions: Set<Interaction> | null = __interactionsRef.current;1196 __interactionsRef.current = root.memoizedInteractions;1197 return prevInteractions;1198 }1199 return null;1200}1201function popInteractions(prevInteractions) {1202 if (enableSchedulerTracing) {1203 __interactionsRef.current = prevInteractions;1204 }1205}1206export function markCommitTimeOfFallback() {1207 globalMostRecentFallbackTime = now();1208}1209export function markSkippedUpdateLanes(lane: Lane | Lanes): void {1210 workInProgressRootSkippedLanes = mergeLanes(1211 lane,1212 workInProgressRootSkippedLanes,1213 );1214}1215export function renderDidSuspend(): void {1216 if (workInProgressRootExitStatus === RootIncomplete) {1217 workInProgressRootExitStatus = RootSuspended;1218 }1219}1220export function renderDidSuspendDelayIfPossible(): void {1221 if (1222 workInProgressRootExitStatus === RootIncomplete ||1223 workInProgressRootExitStatus === RootSuspended1224 ) {1225 workInProgressRootExitStatus = RootSuspendedWithDelay;1226 }1227 // Check if there are updates that we skipped tree that might have unblocked1228 // this render.1229 if (1230 workInProgressRoot !== null &&1231 (includesNonIdleWork(workInProgressRootSkippedLanes) ||1232 includesNonIdleWork(workInProgressRootUpdatedLanes))1233 ) {1234 // Mark the current render as suspended so that we switch to working on1235 // the updates that were skipped. Usually we only suspend at the end of1236 // the render phase.1237 // TODO: We should probably always mark the root as suspended immediately1238 // (inside this function), since by suspending at the end of the render1239 // phase introduces a potential mistake where we suspend lanes that were1240 // pinged or updated while we were rendering.1241 markRootSuspended(workInProgressRoot, workInProgressRootRenderLanes);1242 }1243}1244export function renderDidError() {1245 if (workInProgressRootExitStatus !== RootCompleted) {1246 workInProgressRootExitStatus = RootErrored;1247 }1248}1249// Called during render to determine if anything has suspended.1250// Returns false if we're not sure.1251export function renderHasNotSuspendedYet(): boolean {1252 // If something errored or completed, we can't really be sure,1253 // so those are false.1254 return workInProgressRootExitStatus === RootIncomplete;1255}1256function renderRootSync(root: FiberRoot, lanes: Lanes) {1257 1258 enableLog && console.log('renderRootSync')1259 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('renderRootSync')) debugger1260 1261 const prevExecutionContext = executionContext;1262 executionContext |= RenderContext;1263 const prevDispatcher = pushDispatcher();1264 // If the root or lanes have changed, throw out the existing stack1265 // and prepare a fresh one. Otherwise we'll continue where we left off.1266 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1267 prepareFreshStack(root, lanes);1268 startWorkOnPendingInteractions(root, lanes);1269 }1270 const prevInteractions = pushInteractions(root);1271 do {1272 try {1273 workLoopSync();1274 break;1275 } catch (thrownValue) {1276 handleError(root, thrownValue);1277 }1278 } while (true);1279 resetContextDependencies();1280 if (enableSchedulerTracing) {1281 popInteractions(((prevInteractions: any): Set<Interaction>));1282 }1283 executionContext = prevExecutionContext;1284 popDispatcher(prevDispatcher);1285 if (workInProgress !== null) {1286 // This is a sync render, so we should have finished the whole tree.1287 invariant(1288 false,1289 'Cannot commit an incomplete root. This error is likely caused by a ' +1290 'bug in React. Please file an issue.',1291 );1292 }1293 // Set this to null to indicate there's no in-progress render.1294 workInProgressRoot = null;1295 workInProgressRootRenderLanes = NoLanes;1296 return workInProgressRootExitStatus;1297}1298// The work loop is an extremely hot path. Tell Closure not to inline it.1299/** @noinline */1300function workLoopSync() {1301 1302 enableLog && console.log('workLoopSync')1303 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('workLoopSync')) debugger1304 // Already timed out, so perform work without checking if we need to yield.1305 while (workInProgress !== null) {1306 enableLog && console.log('workLoopSync in while')1307 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('workLoopSync')) debugger1308 performUnitOfWork(workInProgress);1309 }1310}1311function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {1312 1313 enableLog && console.log('renderRootConcurrent start')1314 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('renderRootConcurrent')) debugger1315 const prevExecutionContext = executionContext;1316 executionContext |= RenderContext;1317 const prevDispatcher = pushDispatcher();1318 // If the root or lanes have changed, throw out the existing stack1319 // and prepare a fresh one. Otherwise we'll continue where we left off.1320 // rootæè
laneæ¹åï¼é½ä¼è°ç¨prepareFreshStackå·æ°æ 帧, 丢å¼ä¸ä¸æ¬¡æ¸²æè¿åº¦1321 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1322 resetRenderTimer();1323 // prepareFreshStackéé¢ä¼éè¿workInProgress = createWorkInProgress(root.current, null)å建workInProgress1324 prepareFreshStack(root, lanes);1325 startWorkOnPendingInteractions(root, lanes);1326 }1327 const prevInteractions = pushInteractions(root);1328 do {1329 try {1330 // workLoopConcurrentä¸å¤æè¶
åºæ¶é´çäºï¼1331 // é£workLoopConcurrentå°±ä¼ä»è°ç¨æ å¼¹åºï¼1332 // èµ°å°ä¸é¢çbreakï¼ç»æ¢å¾ªç¯1333 workLoopConcurrent();1334 break;1335 } catch (thrownValue) {1336 handleError(root, thrownValue);1337 }1338 } while (true);1339 // èµ°å°è¿é就说ææ¯è¢«æ¶é´çææä»»å¡äºï¼æè
fiberæ ç´æ¥æ建å®äº1340 // ä¾æ®æ
åµreturnä¸åçstatus1341 resetContextDependencies();1342 if (enableSchedulerTracing) {1343 popInteractions(((prevInteractions: any): Set<Interaction>));1344 }1345 popDispatcher(prevDispatcher);1346 executionContext = prevExecutionContext;1347 // Check if the tree has completed.1348 if (workInProgress !== null) {1349 // workInProgress ä¸ä¸ºnullï¼è¯´ææ¯è¢«æ¶é´çææç1350 // return RootIncomplete说æè¿æ²¡å®æä»»å¡1351 // Still work remaining.1352 enableLog && console.log('renderRootConcurrent end')1353 return RootIncomplete;1354 } else {1355 // å¦å说æä»»å¡å®æäº1356 // returnæç»çstatus1357 // Completed the tree.1358 // Set this to null to indicate there's no in-progress render.1359 workInProgressRoot = null;1360 workInProgressRootRenderLanes = NoLanes;1361 enableLog && console.log('renderRootConcurrent end')1362 // Return the final exit status.1363 return workInProgressRootExitStatus;1364 }1365}1366/** @noinline */1367function workLoopConcurrent() {1368 enableLog && console.log('workLoopConcurrent start')1369 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('workLoopConcurrent')) debugger1370 // è°ç¨shouldYieldå¤æå¦æè¶
åºæ¶é´çéå¶ï¼é£ä¹ç»æ循ç¯1371 // Perform work until Scheduler asks us to yield1372 while (workInProgress !== null && !shouldYield()) {1373 performUnitOfWork(workInProgress);1374 }1375 enableLog && console.log('workLoopConcurrent end')1376}1377function performUnitOfWork(unitOfWork: Fiber): void {1378 1379 enableLog && console.log('performUnitOfWork start')1380 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('performUnitOfWork')) debugger1381 // The current, flushed, state of this fiber is the alternate. Ideally1382 // nothing should rely on this, but relying on it here means that we don't1383 // need an additional field on the work in progress.1384 const current = unitOfWork.alternate;1385 let next;1386 if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {1387 startProfilerTimer(unitOfWork);1388 /**1389 * subtreeRenderLaneså¨prepareFreshStackéé¢æèµå¼1390 * workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;1391 */1392 next = beginWork(current, unitOfWork, subtreeRenderLanes);1393 stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);1394 } else {1395 next = beginWork(current, unitOfWork, subtreeRenderLanes);1396 }1397 // ä¸é¢beginWorkå·²ç»å¤çå®pendingPropsäºï¼é£ä¹ä¹åå°±ä¼èµå¼ç»memorizedProps1398 unitOfWork.memoizedProps = unitOfWork.pendingProps;1399 if (next === null) {1400 // å¦ænext为nullï¼åæå³çâéâè¿ç¨ç»æï¼è¿å
¥âå½âè¿ç¨ï¼completeUnitOfWork1401 // If this doesn't spawn new work, complete the current work.1402 completeUnitOfWork(unitOfWork);1403 } else {1404 /**1405 * å¦åworkInProgressæånextï¼ç¶åç»æå½æ°ï¼ä¹åä¸é¢çworkLoopConcurrent1406 * å¤æå°workInProgressä¸ä¸ºç©ºï¼ä¸æªè¶
åºæ¶é´çéå¶ï¼ä¼ç»§ç»è°ç¨performUnitOfWork1407 */1408 workInProgress = next;1409 }1410 ReactCurrentOwner.current = null;1411 enableLog && console.log('performUnitOfWork end')1412}1413function completeUnitOfWork(unitOfWork: Fiber): void {1414 // å·²ç»ç»æbeginWorké¶æ®µçfiberèç¹è¢«ç§°ä¸ºcompletedWork1415 // Attempt to complete the current unit of work, then move to the next1416 // sibling. If there are no more siblings, return to the parent fiber.1417 let completedWork = unitOfWork;1418 1419 enableLog && console.log('completeUnitOfWork start')1420 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('completeUnitOfWork')) debugger1421 do {1422 // åä¸ä¸ç´å¾ªç¯å°rootçè¿ç¨1423 // The current, flushed, state of this fiber is the alternate. Ideally1424 // nothing should rely on this, but relying on it here means that we don't1425 // need an additional field on the work in progress.1426 const current = completedWork.alternate;1427 const returnFiber = completedWork.return;1428 // Check if the work completed or if something threw.1429 // å¦æcompletedWorkåºéï¼ä¼æä¸Incompleteï¼åèµ°else1430 if ((completedWork.flags & Incomplete) === NoFlags) {1431 // æ£å¸¸å®æäºå·¥ä½1432 let next;1433 if (1434 !enableProfilerTimer || // enableProfilerTimer = __PROFILE__ï¼é»è®¤ä¸ºtrue1435 (completedWork.mode & ProfileMode) === NoMode1436 ) {1437 next = completeWork(current, completedWork, subtreeRenderLanes);1438 } else {1439 startProfilerTimer(completedWork);1440 next = completeWork(current, completedWork, subtreeRenderLanes);1441 // Update render duration assuming we didn't error.1442 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1443 }1444 if (next !== null) {1445 /**1446 * å¦ææåèç¹ï¼åå°workInProgressæå该åèç¹ï¼ç¶åéåºcompleteUnitOfWorkï¼1447 * åå°performUnitOfWorkï¼performUnitOfWorkä¼åå°workLoop(Sync)Concurrent1448 */1449 // Completing this fiber spawned new work. Work on that next.1450 workInProgress = next;1451 return;1452 }1453 } else {1454 // è¿è¡é误å¤ç1455 // This fiber did not complete because something threw. Pop values off1456 // the stack without entering the complete phase. If this is a boundary,1457 // capture values if possible.1458 const next = unwindWork(completedWork, subtreeRenderLanes);1459 // Because this fiber did not complete, don't reset its expiration time.1460 if (next !== null) {1461 // If completing this work spawned new work, do that next. We'll come1462 // back here again.1463 // Since we're restarting, remove anything that is not a host effect1464 // from the effect tag.1465 next.flags &= HostEffectMask;1466 workInProgress = next;1467 return;1468 }1469 if (1470 enableProfilerTimer &&1471 (completedWork.mode & ProfileMode) !== NoMode1472 ) {1473 // Record the render duration for the fiber that errored.1474 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1475 // Include the time spent working on failed children before continuing.1476 let actualDuration = completedWork.actualDuration;1477 let child = completedWork.child;1478 while (child !== null) {1479 actualDuration += child.actualDuration;1480 child = child.sibling;1481 }1482 completedWork.actualDuration = actualDuration;1483 }1484 if (returnFiber !== null) {1485 /**1486 * ç»ç¶fiberæä¸Incompleteï¼ä¹åç¶fiberä¹ä¼èµ°unwindWork1487 * ç´å°æ¾å°é误边ççç¶fiberï¼ä¸é¢çnext !== nullï¼å°workInProgressæå该é误边çï¼ç¶åreturnæ1488 * é误边ççç¶fiberéæ°èµ°beginWorkå¤çupdateQueueï¼æ¹é误边ç为å¢å äºä¸ä¸ªupdateï¼updateçpayloadægetDerivedStateFromError1489 * æ
渲æåºå¤ç¨çui1490 * render() {1491 * if (this.state.hasError) {1492 * return <h1>Something went wrong.</h1>;1493 * }1494 * return this.props.children;1495 * }1496 */1497 // Mark the parent fiber as incomplete1498 returnFiber.flags |= Incomplete;1499 returnFiber.subtreeFlags = NoFlags;1500 returnFiber.deletions = null;1501 }1502 }1503 // ä¸é¢completedWorkçchildå¤çå®äºï¼å¼å§å¤çcompletedWorkçsibling1504 const siblingFiber = completedWork.sibling;1505 if (siblingFiber !== null) {1506 // If there is more work to do in this returnFiber, do that next.1507 workInProgress = siblingFiber;1508 return;1509 }1510 // Otherwise, return to the parent1511 // childåsiblingé½å¤çå®ï¼å°±å溯å°returnFiber1512 completedWork = returnFiber;1513 // Update the next thing we're working on in case something throws.1514 // workInProgressåæåreturnFiber1515 workInProgress = completedWork;1516 } while (completedWork !== null);1517 // We've reached the root.1518 // å·²ç»å°è¾¾æ ¹èç¹ï¼å设置wipæ ¹èç¹éåºçç¶æ为已å®æ1519 if (workInProgressRootExitStatus === RootIncomplete) {1520 workInProgressRootExitStatus = RootCompleted;1521 }1522 enableLog && console.log('completeUnitOfWork end')1523}1524/** commité¶æ®µçèµ·ç¹ */ 1525function commitRoot(root) {1526 1527 enableLog && console.log('commitRoot start')1528 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('commitRoot')) debugger1529 1530 const renderPriorityLevel = getCurrentPriorityLevel();1531 // commité¶æ®µæ¯åæ¥æ§è¡çï¼ä¼å
级æé«1532 runWithPriority(1533 ImmediateSchedulerPriority,1534 commitRootImpl.bind(null, root, renderPriorityLevel),1535 );1536 enableLog && console.log('commitRoot end')1537 return null;1538}1539function commitRootImpl(root, renderPriorityLevel) {1540 // ç±äºuseEffectæ¯å¼æ¥è°åº¦çï¼æå¯è½ä¸ä¸æ¬¡æ´æ°è°åº¦çuseEffectè¿æªè¢«çæ£æ§è¡ï¼1541 // æ以å¨æ¬æ¬¡æ´æ°å¼å§åï¼éè¦å
å°ä¹åçuseEffecté½æ§è¡æï¼ä»¥ä¿è¯æ¬æ¬¡æ´æ°è°åº¦ç1542 // useEffecté½æ¯æ¬æ¬¡æ´æ°äº§çç1543 enableLog && console.log('commitRootImpl start')1544 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('commitRootImpl')) debugger1545 do {1546 // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which1547 // means `flushPassiveEffects` will sometimes result in additional1548 // passive effects. So we need to keep flushing in a loop until there are1549 // no more pending effects.1550 // TODO: Might be better if `flushPassiveEffects` did not automatically1551 // flush synchronous work at the end, to avoid factoring hazards like this.1552 // 触åuseEffectåè°ä¸å
¶ä»åæ¥ä»»å¡ã1553 // ç±äºè¿äºä»»å¡å¯è½è§¦åæ°ç渲æï¼æ以è¿éè¦ä¸ç´éåæ§è¡ç´å°æ²¡æä»»å¡1554 flushPassiveEffects();1555 } while (rootWithPendingPassiveEffects !== null);1556 invariant(1557 (executionContext & (RenderContext | CommitContext)) === NoContext,1558 'Should not already be working.',1559 );1560 // rootæ fiberRootNode1561 // root.finishedWorkæå½ååºç¨çrootFiber1562 const finishedWork = root.finishedWork;1563 const lanes = root.finishedLanes;1564 1565 if (finishedWork === null) {1566 return null;1567 }1568 root.finishedWork = null;1569 root.finishedLanes = NoLanes;1570 invariant(1571 finishedWork !== root.current,1572 'Cannot commit the same tree as before. This error is likely caused by ' +1573 'a bug in React. Please file an issue.',1574 );1575 // commitRoot never returns a continuation; it always finishes synchronously.1576 // So we can clear these now to allow a new callback to be scheduled.1577 // éç½®Schedulerç»å®çåè°å½æ°1578 root.callbackNode = null;1579 // Update the first and last pending times on this root. The new first1580 // pending time is whatever is left on the root fiber.1581 // å°æ¶éå°çchildLanesï¼è¿årootèªå·±çlanesï¼ä¸å¹¶èµå¼ç»remainingLanes1582 let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);1583 /**1584 * å°root.pendingLaneså»æremaingLanes(å«ånoLongerPendingLanesï¼ï¼1585 * å³noLongerPendingLanes = root.pendingLanes & ~remainingLanesï¼1586 * ä¹åroot.pendingLanes = remainingLanes1587 * å°noLongerPendingLanes对åºeventTimesãexpirationTimesä¸çindexä½çå¼ç½®ä¸ºNoTimestamp1588 */1589 markRootFinished(root, remainingLanes);1590 // Clear already finished discrete updates in case that a later call of1591 // `flushDiscreteUpdates` starts a useless render pass which may cancels1592 // a scheduled timeout.1593 // æ¸
é¤å·²ç»å®æç离æ£æ´æ°ï¼å¦clickæ´æ°1594 if (rootsWithPendingDiscreteUpdates !== null) {1595 if (1596 !hasDiscreteLanes(remainingLanes) &&1597 rootsWithPendingDiscreteUpdates.has(root)1598 ) {1599 // å¦æä¸æ¯ç¦»æ£æ´æ°ä¸ä¹årootsWithPendingDiscreteUpdateséæroot1600 // åå»æroot1601 rootsWithPendingDiscreteUpdates.delete(root);1602 }1603 }1604 if (root === workInProgressRoot) {1605 // We can reset these now that they are finished.1606 // å·²ç»å®æï¼åéç½®1607 workInProgressRoot = null;1608 workInProgress = null;1609 workInProgressRootRenderLanes = NoLanes;1610 } else {1611 // This indicates that the last root we worked on is not the same one that1612 // we're committing now. This most commonly happens when a suspended root1613 // times out.1614 }1615 // Check if there are any effects in the whole tree.1616 // TODO: This is left over from the effect list implementation, where we had1617 // to check for the existence of `firstEffect` to satsify Flow. I think the1618 // only other reason this optimization exists is because it affects profiling.1619 // Reconsider whether this is necessary.1620 // åæ æ¯å¦æå¯ä½ç¨1621 const subtreeHasEffects =1622 (finishedWork.subtreeFlags &1623 (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1624 NoFlags;1625 // rootæ¯å¦æå¯ä½ç¨1626 const rootHasEffect =1627 (finishedWork.flags &1628 (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1629 NoFlags;1630 // åæ ææ ¹èç¹æå¯ä½ç¨1631 if (subtreeHasEffects || rootHasEffect) {1632 let previousLanePriority;1633 // å°å½åä¸ä¸ææ 记为CommitContextï¼ä½ä¸ºcommité¶æ®µçæ å¿1634 const prevExecutionContext = executionContext;1635 executionContext |= CommitContext;1636 // è·åä¸æ¬¡__interactionsRef.currentï¼Setéåï¼1637 const prevInteractions = pushInteractions(root);1638 // Reset this to null before calling lifecycles1639 // è°ç¨çå½å¨æåéç½®1640 ReactCurrentOwner.current = null;1641 // commitå为å 个é¶æ®µ1642 // The commit phase is broken into several sub-phases. We do a separate pass1643 // of the effect list for each phase: all mutation effects come before all1644 // layout effects, and so on.1645 /**1646 * 第ä¸é¶æ®µä¸ºï¼before mutationï¼ï¼æ§è¡DOMæä½åï¼ï¼before mutationç´æ¥ç¿»è¯æ¯åååï¼1647 * ä¼å¨commitBeforeMutationEffectsImpléé¢å¤ç带æSnapshot,Passiveæ è®°çfiberèç¹ï¼1648 * å³å¦æ带æSnapshotï¼åå¨domåååä¼è·åæ ¹èç¹ä¸çstate,è°ç¨getSnapshotBeforeUpdateï¼1649 * 带æPassiveï¼åä¼è°ç¨flushPassiveEffects,å¦ä¸1650 * scheduleCallback(NormalSchedulerPriority, () => {1651 * flushPassiveEffects();1652 * return null;1653 * })1654 */1655 // The first phase a "before mutation" phase. We use this phase to read the1656 // state of the host tree right before we mutate it. This is where1657 // getSnapshotBeforeUpdate is called.1658 focusedInstanceHandle = prepareForCommit(root.containerInfo);1659 shouldFireAfterActiveInstanceBlur = false;1660 commitBeforeMutationEffects(finishedWork);1661 // We no longer need to track the active instance fiber1662 focusedInstanceHandle = null;1663 if (enableProfilerTimer) {1664 // è®°å½å½åcommitçæ¶é´1665 // Mark the current commit time to be shared by all Profilers in this1666 // batch. This enables them to be grouped later.1667 recordCommitTime();1668 }1669 /**1670 * 第äºé¶æ®µï¼mutationé¶æ®µï¼æ§è¡DOMæä½ï¼ï¼mutationç´æ¥ç¿»è¯æ¯è£åï¼å³é¡µé¢åçæ¹åï¼è¿ä¸ªé¶æ®µdomæ´æ°äºï¼1671 * ä¼å¤ç带æPlacement, Update, Deletion, Hydrating, ContentReset, Refçflagçfiber1672 */1673 // The next phase is the mutation phase, where we mutate the host tree.1674 commitMutationEffects(finishedWork, root, renderPriorityLevel);1675 if (shouldFireAfterActiveInstanceBlur) {1676 afterActiveInstanceBlur();1677 }1678 // commitåéç½®ä¸äºç¶æ1679 resetAfterCommit(root.containerInfo);1680 // The work-in-progress tree is now the current tree. This must come after1681 // the mutation phase, so that the previous tree is still current during1682 // componentWillUnmount, but before the layout phase, so that the finished1683 // work is current during componentDidMount/Update.1684 // å°è¿æ¥workInProgress treeå为current tree1685 // 1.è¿ä¸æ¥å¿
é¡»å¨mutationé¶æ®µåï¼ä¸ºäºå¨ç»ä»¶å¨è°ç¨componentWillUnmountæ¶currentæ ä¾ç¶åå¨1686 // 2.è¿ä¸æ¥åå¿
é¡»å¨layoutèç¹ä¹åï¼å 为componentDidMount/Updateè¦è·åçæ¯å½åfinishedWorkçç¶æ1687 root.current = finishedWork;1688 // The next phase is the layout phase, where we call effects that read1689 // the host tree after it's been mutated. The idiomatic use case for this is1690 // layout, but class component lifecycles also fire here for legacy reasons.1691 /**1692 * 第ä¸é¶æ®µï¼layouté¶æ®µï¼domåæ´åï¼ ä¼å¤ç带æUpdate | Callback | Refçflagçfiber1693 */1694 try {1695 recursivelyCommitLayoutEffects(finishedWork, root);1696 } catch (error) {1697 captureCommitPhaseErrorOnRoot(finishedWork, finishedWork, error);1698 }1699 // If there are pending passive effects, schedule a callback to process them.1700 // PassiveMaskï¼å
å«äºDeletionçeffectï¼å æ¤ï¼æ¤å¤è°åº¦useEffectæ¯ä¸ºäºè¢«å¸è½½çç»ä»¶å»è°åº¦ç1701 // commité¶æ®µæ¶åå°useEffectçå°æ¹æä¸å¤ï¼1702 // commité¶æ®µåå¼å§ï¼è¿ä¸ªæ¶å主è¦æ¯æ§è¡æ¬æ¬¡æ´æ°ä¹åè¿æªè¢«è§¦åçuseEffectï¼ç¸å½äºæ¸
çå·¥ä½ãå 为è¦ä¿è¯æ¬æ¬¡è°åº¦çuseEffecté½æ¯æ¬æ¬¡æ´æ°äº§çç1703 // commité¶æ®µä¹å
çbeforeMutationé¶æ®µï¼è¿ä¸ªæ¶å为å«æuseEffectçç»ä»¶è°åº¦useEffect1704 // commité¶æ®µä¹å
çlayouté¶æ®µï¼ä¸ºå¸è½½çç»ä»¶è°åº¦useEffectï¼æ§è¡effectçdestroyå½æ°ï¼æ¸
çeffect1705 // PassiveMask === 0b000000,0010,0000,1000;1706 if (1707 (finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||1708 (finishedWork.flags & PassiveMask) !== NoFlags1709 ) {1710 /**1711 * commitBeforeMutationEffectsä¸çcommitBeforeMutationEffectsImplè°ç¨äºï¼1712 * ä¼å°rootDoesHavePassiveEffects设为trueï¼é£ä¹è¿éå°±ä¸ç¨æ§è¡äºï¼ç¸å½äºä¸ä¸ªå¼å
³ã1713 * 两个é¶æ®µåªæä¸ä¸ªå¼æ¥è°åº¦flushPassiveEffectsæ¥å¤çuseEffect1714 */1715 if (!rootDoesHavePassiveEffects) {1716 rootDoesHavePassiveEffects = true;1717 // layouté¶æ®µè°åº¦useEffect1718 scheduleCallback(NormalSchedulerPriority, () => {1719 flushPassiveEffects();1720 return null;1721 });1722 }1723 }1724 // Tell Scheduler to yield at the end of the frame, so the browser has an1725 // opportunity to paint.1726 requestPaint();1727 if (enableSchedulerTracing) { // enableSchedulerTracing = __PROFILE__1728 popInteractions(((prevInteractions: any): Set<Interaction>));1729 }1730 executionContext = prevExecutionContext;1731 } else {1732 // No effects.1733 // 没æeffectListï¼ç´æ¥å°wprkInProgressæ åæ¢ä¸ºcurrentæ 1734 root.current = finishedWork;1735 // Measure these anyway so the flamegraph explicitly shows that there were1736 // no effects.1737 // TODO: Maybe there's a better way to report this.1738 if (enableProfilerTimer) {1739 recordCommitTime();1740 }1741 }1742 const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;1743 if (rootDoesHavePassiveEffects) {1744 // 为true说ææå¯ä½ç¨ï¼å次ä¿åç¶æï¼ä½æ¯ä¸è°åº¦1745 // This commit has passive effects. Stash a reference to them. But don't1746 // schedule a callback until after flushing layout work.1747 rootDoesHavePassiveEffects = false;1748 rootWithPendingPassiveEffects = root;1749 pendingPassiveEffectsLanes = lanes;1750 pendingPassiveEffectsRenderPriority = renderPriorityLevel;1751 }1752 // è·åå°æªå¤ççä¼å
级ï¼æ¯å¦ä¹å被跳è¿çä»»å¡çä¼å
级1753 // Read this again, since an effect might have updated it1754 remainingLanes = root.pendingLanes;1755 // Check if there's remaining work on this root1756 if (remainingLanes !== NoLanes) {1757 if (enableSchedulerTracing) {1758 if (spawnedWorkDuringRender !== null) {1759 const expirationTimes = spawnedWorkDuringRender;1760 spawnedWorkDuringRender = null;1761 for (let i = 0; i < expirationTimes.length; i++) {1762 scheduleInteractions(1763 root,1764 expirationTimes[i],1765 root.memoizedInteractions,1766 );1767 }1768 }1769 schedulePendingInteractions(root, remainingLanes);1770 }1771 } else {1772 // If there's no remaining work, we can clear the set of already failed1773 // error boundaries.1774 legacyErrorBoundariesThatAlreadyFailed = null;1775 }1776 if (enableSchedulerTracing) {1777 if (!rootDidHavePassiveEffects) {1778 // å¦æ没æå¯ä½ç¨1779 // If there are no passive effects, then we can complete the pending interactions.1780 // Otherwise, we'll wait until after the passive effects are flushed.1781 // Wait to do this until after remaining work has been scheduled,1782 // so that we don't prematurely signal complete for interactions when there's e.g. hidden work.1783 finishPendingInteractions(root, lanes);1784 }1785 }1786 if (remainingLanes === SyncLane) {1787 // 计ç®æ ¹èç¹åæ¥re-renderç次æ°ï¼å¤ªå¤æå³çæ¯ä¸ªæ»å¾ªç¯1788 // Count the number of times the root synchronously re-renders without1789 // finishing. If there are too many, it indicates an infinite update loop.1790 if (root === rootWithNestedUpdates) {1791 nestedUpdateCount++;1792 } else {1793 nestedUpdateCount = 0;1794 rootWithNestedUpdates = root;1795 }1796 } else {1797 nestedUpdateCount = 0;1798 }1799 onCommitRootDevTools(finishedWork.stateNode, renderPriorityLevel);1800 /*1801 * æ¯æ¬¡commité¶æ®µå®æåï¼åæ§è¡ä¸éensureRootIsScheduledï¼1802 * ä¿è¯rootä¸ä»»ä½çpendingLanesé½è½è¢«å¤ç,å³ç¡®ä¿æ¯å¦è¿æä»»å¡éè¦è¢«è°åº¦ã1803 * ä¾å¦ï¼é«ä¼å
级æéçæ´æ°å®æåï¼commitå®æåï¼è¿ä¼åæ§è¡ä¸éï¼ä¿è¯ä¹åè·³è¿çä½ä¼å
级任å¡1804 * éæ°è°åº¦ï¼æè
ä¸é¢çLayoutèç¹è°ç¨recursivelyCommitLayoutEffectséé¢å¨ComponentDid(Mount)Update1805 * éé¢åè°ç¨äºsetStateä¹ç±»çï¼é£ä¸é¢å°±è¦åæ£æµä¸ä¸ï¼æåå次åèµ·è°åº¦1806 * */1807 // Always call this before exiting `commitRoot`, to ensure that any1808 // additional work on this root is scheduled.1809 ensureRootIsScheduled(root, now());1810 if (hasUncaughtError) {1811 hasUncaughtError = false;1812 const error = firstUncaughtError;1813 firstUncaughtError = null;1814 throw error;1815 }1816 // If layout work was scheduled, flush it now.1817 // æ£æµåæ¥ä»»å¡ï¼æå主å¨è°ç¨flushSyncCallbackQueue(æ éå次çå¾
schedulerè°åº¦), 1818 // å次è¿å
¥fiberæ æé 循ç¯1819 flushSyncCallbackQueue();1820 enableLog && console.log('commitRootImpl end')1821 return null;1822}1823function commitBeforeMutationEffects(firstChild: Fiber) {1824 enableLog && console.log('commitBeforeMutationEffects start')1825 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('commitBeforeMutationEffects')) debugger1826 let fiber = firstChild;1827 while (fiber !== null) {1828 if (fiber.deletions !== null) {1829 commitBeforeMutationEffectsDeletions(fiber.deletions);1830 }1831 if (fiber.child !== null) {1832 // BeforeMutationMask: 0b0000000011000010101833 const primarySubtreeFlags = fiber.subtreeFlags & BeforeMutationMask;1834 if (primarySubtreeFlags !== NoFlags) {1835 commitBeforeMutationEffects(fiber.child);1836 }1837 }1838 try {1839 commitBeforeMutationEffectsImpl(fiber);1840 } catch (error) {1841 captureCommitPhaseError(fiber, fiber.return, error);1842 }1843 fiber = fiber.sibling;1844 }1845 enableLog && console.log('commitBeforeMutationEffects end')1846}1847function commitBeforeMutationEffectsImpl(fiber: Fiber) {1848 enableLog && console.log('commitBeforeMutationEffectsImpl start')1849 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('commitBeforeMutationEffectsImpl')) debugger1850 const current = fiber.alternate;1851 const flags = fiber.flags;1852 if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {1853 // Check to see if the focused element was inside of a hidden (Suspense) subtree.1854 // TODO: Move this out of the hot path using a dedicated effect tag.1855 if (1856 fiber.tag === SuspenseComponent &&1857 isSuspenseBoundaryBeingHidden(current, fiber) &&1858 doesFiberContain(fiber, focusedInstanceHandle)1859 ) {1860 shouldFireAfterActiveInstanceBlur = true;1861 beforeActiveInstanceBlur();1862 }1863 }1864 // Snapshot = 0b000000000,100000000;1865 if ((flags & Snapshot) !== NoFlags) {1866 // éè¿commitBeforeMutationEffectOnFiberè°ç¨getSnapshotBeforeUpdate1867 commitBeforeMutationEffectOnFiber(current, fiber);1868 }1869 // Passive = 0b000000001,000000000;1870 if ((flags & Passive) !== NoFlags) {1871 // If there are passive effects, schedule a callback to flush at1872 // the earliest opportunity.1873 // before mutation è°åº¦useEffects1874 if (!rootDoesHavePassiveEffects) {1875 rootDoesHavePassiveEffects = true;1876 scheduleCallback(NormalSchedulerPriority, () => {1877 flushPassiveEffects();1878 return null;1879 });1880 }1881 }1882 enableLog && console.log('commitBeforeMutationEffectsImpl end')1883}1884function commitBeforeMutationEffectsDeletions(deletions: Array<Fiber>) {1885 for (let i = 0; i < deletions.length; i++) {1886 const fiber = deletions[i];1887 // TODO (effects) It would be nice to avoid calling doesFiberContain()1888 // Maybe we can repurpose one of the subtreeFlags positions for this instead?1889 // Use it to store which part of the tree the focused instance is in?1890 // This assumes we can safely determine that instance during the "render" phase.1891 if (doesFiberContain(fiber, ((focusedInstanceHandle: any): Fiber))) {1892 shouldFireAfterActiveInstanceBlur = true;1893 beforeActiveInstanceBlur();1894 }1895 }1896}1897function commitMutationEffects(1898 firstChild: Fiber,1899 root: FiberRoot,1900 renderPriorityLevel: ReactPriorityLevel,1901) {1902 enableLog && console.log('commitMutationEffects start')1903 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('commitMutationEffects')) debugger1904 let fiber = firstChild;1905 while (fiber !== null) {1906 const deletions = fiber.deletions;1907 if (deletions !== null) {1908 // 该Fiberèç¹å¯¹åºçDOMèç¹éè¦ä»é¡µé¢ä¸å é¤1909 commitMutationEffectsDeletions(1910 deletions,1911 fiber,1912 root,1913 renderPriorityLevel,1914 );1915 }1916 // å¦æå é¤ä¹åçfiberè¿æåèç¹ï¼1917 // éå½è°ç¨commitMutationEffectsæ¥å¤ç1918 if (fiber.child !== null) {1919 const mutationFlags = fiber.subtreeFlags & MutationMask;1920 if (mutationFlags !== NoFlags) {1921 // subtreeFlagså±äºPlacement | Update | Deletion | ContentReset | Ref | Hydratingä¸ä¸ä¸ª1922 commitMutationEffects(fiber.child, root, renderPriorityLevel);1923 }1924 }1925 try {1926 commitMutationEffectsImpl(fiber, root, renderPriorityLevel);1927 } catch (error) {1928 captureCommitPhaseError(fiber, fiber.return, error);1929 }1930 fiber = fiber.sibling;1931 }1932 enableLog && console.log('commitMutationEffects end')1933}1934function commitMutationEffectsImpl(1935 fiber: Fiber,1936 root: FiberRoot,1937 renderPriorityLevel,1938) {1939 enableLog && console.log('commitMutationEffectsImpl start')1940 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('commitMutationEffectsImpl')) debugger1941 const flags = fiber.flags;1942 if (flags & ContentReset) { // ContentReset = 0b0000000000,0001,0000;1943 commitResetTextContent(fiber);1944 }1945 if (flags & Ref) { // Ref = 0b0000000000,1000,0000;1946 const current = fiber.alternate;1947 if (current !== null) {1948 // 移é¤ä¹åçref1949 commitDetachRef(current);1950 }1951 }1952 // The following switch statement is only concerned about placement,1953 // updates, and deletions. To avoid needing to add a case for every possible1954 // bitmap value, we remove the secondary effects from the effect tag and1955 // switch on that value.1956 const primaryFlags = flags & (Placement | Update | Hydrating);1957 // Placement = 0b000000,0000,0000,0010;1958 // Update = 0b000000,0000,0000,0100;1959 // PlacementAndUpdate = 0b000000,0000,0000,0110;1960 // Hydrating = 0b000000,0100,0000,0000;1961 switch (primaryFlags) {1962 case Placement: {1963 commitPlacement(fiber);1964 // Clear the "placement" from effect tag so that we know that this is1965 // inserted, before any life-cycles like componentDidMount gets called.1966 // TODO: findDOMNode doesn't rely on this any more but isMounted does1967 // and isMounted is deprecated anyway so we should be able to kill this.1968 // å·²ç»Placementå®äºï¼åå»æ Placement flag1969 fiber.flags &= ~Placement;1970 break;1971 }1972 case PlacementAndUpdate: {1973 // Placement1974 commitPlacement(fiber);1975 // Clear the "placement" from effect tag so that we know that this is1976 // inserted, before any life-cycles like componentDidMount gets called.1977 fiber.flags &= ~Placement;1978 // Update1979 const current = fiber.alternate;1980 commitWork(current, fiber);1981 break;1982 }1983 case Hydrating: {1984 fiber.flags &= ~Hydrating;1985 break;1986 }1987 case HydratingAndUpdate: {1988 fiber.flags &= ~Hydrating;1989 // Update1990 const current = fiber.alternate;1991 commitWork(current, fiber);1992 break;1993 }1994 case Update: {1995 /**1996 * æ´æ°ï¼ useEffect,useLayoutEffecté½ä¼è®¾ç½®Updateæ è®°,1997 * ä¸è¿è¿éåªä¼å¤çuseLayoutEffectï¼1998 * å 为commitWorkéé¢è°ç¨çcommitHookEffectListUnmountä¼ å
¥ç1999 * 第ä¸ä¸ªåæ°æ¯HookLayout | HookHasEffect2000 */2001 const current = fiber.alternate;2002 commitWork(current, fiber);2003 break;2004 }2005 }2006 enableLog && console.log('commitMutationEffectsImpl end')2007}2008function commitMutationEffectsDeletions(2009 deletions: Array<Fiber>,2010 nearestMountedAncestor: Fiber,2011 root: FiberRoot,2012 renderPriorityLevel,2013) {2014 for (let i = 0; i < deletions.length; i++) {2015 const childToDelete = deletions[i];2016 try {2017 commitDeletion(2018 root,2019 childToDelete,2020 nearestMountedAncestor,2021 renderPriorityLevel,2022 );2023 } catch (error) {2024 captureCommitPhaseError(childToDelete, nearestMountedAncestor, error);2025 }2026 }2027}2028export function schedulePassiveEffectCallback() {2029 if (!rootDoesHavePassiveEffects) {2030 rootDoesHavePassiveEffects = true;2031 scheduleCallback(NormalSchedulerPriority, () => {2032 flushPassiveEffects();2033 return null;2034 });2035 }2036}2037// è¿åå¯ä½ç¨æ¯å¦è¢«æ¸
空çæ å¿2038export function flushPassiveEffects(): boolean {2039 enableLog && console.log('flushPassiveEffects start')2040 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('flushPassiveEffects')) debugger2041 // Returns whether passive effects were flushed.2042 /**2043 * pendingPassiveEffectsRenderPriorityä¼å¨commitRootImpléé¢rootDoesHavePassiveEffects为trueçæ¶åèµå¼ä¸ºï¼2044 * pendingPassiveEffectsRenderPriority = renderPriorityLevel2045 */2046 2047 if (pendingPassiveEffectsRenderPriority !== NoSchedulerPriority) {2048 const priorityLevel = Math.min(pendingPassiveEffectsRenderPriority, NormalSchedulerPriority)2049 pendingPassiveEffectsRenderPriority = NoSchedulerPriority;2050 const priority = runWithPriority(priorityLevel, flushPassiveEffectsImpl);2051 2052 enableLog && console.log('flushPassiveEffects end')2053 return priority2054 }2055 enableLog && console.log('flushPassiveEffects end')2056 return false;2057}2058function flushPassiveMountEffects(root, firstChild: Fiber): void {2059 let fiber = firstChild;2060 while (fiber !== null) {2061 const primarySubtreeFlags = fiber.subtreeFlags & PassiveMask;2062 if (fiber.child !== null && primarySubtreeFlags !== NoFlags) {2063 flushPassiveMountEffects(root, fiber.child);2064 }2065 if ((fiber.flags & Passive) !== NoFlags) {2066 try {2067 commitPassiveMountOnFiber(root, fiber);2068 } catch (error) {2069 captureCommitPhaseError(fiber, fiber.return, error);2070 }2071 }2072 fiber = fiber.sibling;2073 }2074}2075function flushPassiveUnmountEffects(firstChild: Fiber): void {2076 let fiber = firstChild;2077 // 深度ä¼å
éåfiberï¼å¤çå é¤çèç¹ï¼æ§è¡useEffectçdestroyå½æ°2078 while (fiber !== null) {2079 const deletions = fiber.deletions;2080 if (deletions !== null) {2081 for (let i = 0; i < deletions.length; i++) {2082 const fiberToDelete = deletions[i];2083 flushPassiveUnmountEffectsInsideOfDeletedTree(fiberToDelete, fiber);2084 // Now that passive effects have been processed, it's safe to detach lingering pointers.2085 detachFiberAfterEffects(fiberToDelete);2086 }2087 }2088 const child = fiber.child;2089 if (child !== null) {2090 // If any children have passive effects then traverse the subtree.2091 // Note that this requires checking subtreeFlags of the current Fiber,2092 // rather than the subtreeFlags/effectsTag of the first child,2093 // since that would not cover passive effects in siblings.2094 const passiveFlags = fiber.subtreeFlags & PassiveMask;2095 if (passiveFlags !== NoFlags) {2096 flushPassiveUnmountEffects(child);2097 }2098 }2099 // 为fiberæ¬èº«æ§è¡useEffect2100 const primaryFlags = fiber.flags & Passive;2101 if (primaryFlags !== NoFlags) {2102 commitPassiveUnmountOnFiber(fiber);2103 }2104 fiber = fiber.sibling;2105 }2106}2107function flushPassiveUnmountEffectsInsideOfDeletedTree(2108 fiberToDelete: Fiber,2109 nearestMountedAncestor: Fiber,2110): void {2111 if ((fiberToDelete.subtreeFlags & PassiveStatic) !== NoFlags) {2112 // If any children have passive effects then traverse the subtree.2113 // Note that this requires checking subtreeFlags of the current Fiber,2114 // rather than the subtreeFlags/effectsTag of the first child,2115 // since that would not cover passive effects in siblings.2116 let child = fiberToDelete.child;2117 while (child !== null) {2118 flushPassiveUnmountEffectsInsideOfDeletedTree(2119 child,2120 nearestMountedAncestor,2121 );2122 child = child.sibling;2123 }2124 }2125 if ((fiberToDelete.flags & PassiveStatic) !== NoFlags) {2126 commitPassiveUnmountInsideDeletedTreeOnFiber(2127 fiberToDelete,2128 nearestMountedAncestor,2129 );2130 }2131}2132function flushPassiveEffectsImpl() {2133 2134 enableLog && console.log('flushPassiveEffectsImpl start')2135 if (!__LOG_NAMES__.length || __LOG_NAMES__.includes('flushPassiveEffectsImpl')) debugger2136 2137 /** rootWithPendingPassiveEffectsæ¯ å¨commitRootImplä¸2138 * éè¿å¤æ if (rootDoesHavePassiveEffects)ï¼trueçè¯åå°2139 * rootWithPendingPassiveEffectsèµå¼ä¸ºå¨commitRootImplä¸ä¼ å
¥çroot2140 * */2141 // å
æ ¡éªï¼å¦ærootä¸æ²¡æ Passive efectTagçèç¹ï¼åç´æ¥return2142 if (rootWithPendingPassiveEffects === null) {2143 enableLog && console.log('flushPassiveEffectsImpl end')2144 return false;2145 }2146 const root = rootWithPendingPassiveEffects;2147 const lanes = pendingPassiveEffectsLanes;2148 // ä¸é¢å·²ç»æ¿å°äºï¼åå°å
¨å±åééç½®2149 rootWithPendingPassiveEffects = null;2150 pendingPassiveEffectsLanes = NoLanes;2151 invariant(2152 (executionContext & (RenderContext | CommitContext)) === NoContext,2153 'Cannot flush passive effects while already rendering.',2154 );2155 const prevExecutionContext = executionContext;2156 executionContext |= CommitContext;2157 const prevInteractions = pushInteractions(root);2158 // It's important that ALL pending passive effect destroy functions are called2159 // before ANY passive effect create functions are called.2160 // Otherwise effects in sibling components might interfere with each other.2161 // e.g. a destroy function in one component may unintentionally override a ref2162 // value set by a create function in another component.2163 // Layout effects have the same constraint.2164 // å
å¤çunMountï¼å¥½äºååå¤çMountçeffect2165 flushPassiveUnmountEffects(root.current);2166 flushPassiveMountEffects(root, root.current);2167 if (enableSchedulerTracing) {2168 popInteractions(prevInteractions); // prevInteractions as Set<Interaction>2169 finishPendingInteractions(root, lanes);2170 }2171 executionContext = prevExecutionContext;2172 // å·æ°åæ¥ä»»å¡éå2173 flushSyncCallbackQueue();2174 // If additional passive effects were scheduled, increment a counter. If this2175 // exceeds the limit, we'll fire a warning.2176 nestedPassiveUpdateCount =2177 rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;2178 enableLog && console.log('flushPassiveEffectsImpl end')2179 return true;2180}2181export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {2182 return (2183 legacyErrorBoundariesThatAlreadyFailed !== null &&2184 legacyErrorBoundariesThatAlreadyFailed.has(instance)2185 );2186}2187export function markLegacyErrorBoundaryAsFailed(instance: mixed) {2188 if (legacyErrorBoundariesThatAlreadyFailed === null) {2189 legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);2190 } else {2191 legacyErrorBoundariesThatAlreadyFailed.add(instance);2192 }2193}2194function prepareToThrowUncaughtError(error: mixed) {2195 if (!hasUncaughtError) {2196 hasUncaughtError = true;2197 firstUncaughtError = error;2198 }2199}2200export const onUncaughtError = prepareToThrowUncaughtError;2201function captureCommitPhaseErrorOnRoot(2202 rootFiber: Fiber,2203 sourceFiber: Fiber,2204 error: mixed,2205) {2206 const errorInfo = createCapturedValue(error, sourceFiber);2207 const update = createRootErrorUpdate(rootFiber, errorInfo, (SyncLane: Lane));2208 enqueueUpdate(rootFiber, update);2209 const eventTime = requestEventTime();2210 const root = markUpdateLaneFromFiberToRoot(rootFiber, (SyncLane: Lane));2211 if (root !== null) {2212 markRootUpdated(root, SyncLane, eventTime);2213 ensureRootIsScheduled(root, eventTime);2214 schedulePendingInteractions(root, SyncLane);2215 }2216}2217export function captureCommitPhaseError(2218 sourceFiber: Fiber,2219 nearestMountedAncestor: Fiber | null,2220 error: mixed,2221) {2222 if (sourceFiber.tag === HostRoot) {2223 // Error was thrown at the root. There is no parent, so the root2224 // itself should capture it.2225 captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);2226 return;2227 }2228 let fiber = null;2229 fiber = sourceFiber.return;2230 while (fiber !== null) {2231 if (fiber.tag === HostRoot) {2232 captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);2233 return;2234 } else if (fiber.tag === ClassComponent) {2235 const ctor = fiber.type;2236 const instance = fiber.stateNode;2237 if (2238 typeof ctor.getDerivedStateFromError === 'function' ||2239 (typeof instance.componentDidCatch === 'function' &&2240 !isAlreadyFailedLegacyErrorBoundary(instance))2241 ) {2242 const errorInfo = createCapturedValue(error, sourceFiber);2243 const update = createClassErrorUpdate(2244 fiber,2245 errorInfo,2246 (SyncLane: Lane),2247 );2248 enqueueUpdate(fiber, update);2249 const eventTime = requestEventTime();2250 const root = markUpdateLaneFromFiberToRoot(fiber, (SyncLane: Lane));2251 if (root !== null) {2252 markRootUpdated(root, SyncLane, eventTime);2253 ensureRootIsScheduled(root, eventTime);2254 schedulePendingInteractions(root, SyncLane);2255 }2256 return;2257 }2258 }2259 fiber = fiber.return;2260 }2261}2262export function pingSuspendedRoot(2263 root: FiberRoot,2264 wakeable: Wakeable,2265 pingedLanes: Lanes,2266) {2267 const pingCache = root.pingCache;2268 if (pingCache !== null) {2269 // The wakeable resolved, so we no longer need to memoize, because it will2270 // never be thrown again.2271 pingCache.delete(wakeable);2272 }2273 const eventTime = requestEventTime();2274 markRootPinged(root, pingedLanes, eventTime);2275 if (2276 workInProgressRoot === root &&2277 isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)2278 ) {2279 // Received a ping at the same priority level at which we're currently2280 // rendering. We might want to restart this render. This should mirror2281 // the logic of whether or not a root suspends once it completes.2282 // TODO: If we're rendering sync either due to Sync, Batched or expired,2283 // we should probably never restart.2284 // If we're suspended with delay, or if it's a retry, we'll always suspend2285 // so we can always restart.2286 if (2287 workInProgressRootExitStatus === RootSuspendedWithDelay ||2288 (workInProgressRootExitStatus === RootSuspended &&2289 includesOnlyRetries(workInProgressRootRenderLanes) &&2290 now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS)2291 ) {2292 // Restart from the root.2293 prepareFreshStack(root, NoLanes);2294 } else {2295 // Even though we can't restart right now, we might get an2296 // opportunity later. So we mark this render as having a ping.2297 workInProgressRootPingedLanes = mergeLanes(2298 workInProgressRootPingedLanes,2299 pingedLanes,2300 );2301 }2302 }2303 ensureRootIsScheduled(root, eventTime);2304 schedulePendingInteractions(root, pingedLanes);2305}2306function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {2307 // The boundary fiber (a Suspense component or SuspenseList component)...
ReactFiberWorkLoop.old.js
Source:ReactFiberWorkLoop.old.js
...869 }870 }871 if (exitStatus === RootFatalErrored) {872 const fatalError = workInProgressRootFatalError;873 prepareFreshStack(root, NoLanes);874 markRootSuspended(root, lanes);875 ensureRootIsScheduled(root, now());876 throw fatalError;877 }878 if (exitStatus === RootDidNotComplete) {879 // The render unwound without completing the tree. This happens in special880 // cases where need to exit the current render without producing a881 // consistent tree or committing.882 //883 // This should only happen during a concurrent render, not a discrete or884 // synchronous update. We should have already checked for this when we885 // unwound the stack.886 markRootSuspended(root, lanes);887 } else {888 // The render completed.889 // Check if this render may have yielded to a concurrent event, and if so,890 // confirm that any newly rendered stores are consistent.891 // TODO: It's possible that even a concurrent render may never have yielded892 // to the main thread, if it was fast enough, or if it expired. We could893 // skip the consistency check in that case, too.894 const renderWasConcurrent = !includesBlockingLane(root, lanes);895 const finishedWork: Fiber = (root.current.alternate: any);896 if (897 renderWasConcurrent &&898 !isRenderConsistentWithExternalStores(finishedWork)899 ) {900 // A store was mutated in an interleaved event. Render again,901 // synchronously, to block further mutations.902 exitStatus = renderRootSync(root, lanes);903 // We need to check again if something threw904 if (exitStatus === RootErrored) {905 const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);906 if (errorRetryLanes !== NoLanes) {907 lanes = errorRetryLanes;908 exitStatus = recoverFromConcurrentError(root, errorRetryLanes);909 // We assume the tree is now consistent because we didn't yield to any910 // concurrent events.911 }912 }913 if (exitStatus === RootFatalErrored) {914 const fatalError = workInProgressRootFatalError;915 prepareFreshStack(root, NoLanes);916 markRootSuspended(root, lanes);917 ensureRootIsScheduled(root, now());918 throw fatalError;919 }920 }921 // We now have a consistent tree. The next step is either to commit it,922 // or, if something suspended, wait to commit it after a timeout.923 root.finishedWork = finishedWork;924 root.finishedLanes = lanes;925 finishConcurrentRender(root, exitStatus, lanes);926 }927 }928 ensureRootIsScheduled(root, now());929 if (root.callbackNode === originalCallbackNode) {930 // The task node scheduled for this root is the same one that's931 // currently executed. Need to return a continuation.932 return performConcurrentWorkOnRoot.bind(null, root);933 }934 return null;935}936function recoverFromConcurrentError(root, errorRetryLanes) {937 // If an error occurred during hydration, discard server response and fall938 // back to client side render.939 // Before rendering again, save the errors from the previous attempt.940 const errorsFromFirstAttempt = workInProgressRootConcurrentErrors;941 if (isRootDehydrated(root)) {942 // The shell failed to hydrate. Set a flag to force a client rendering943 // during the next attempt. To do this, we call prepareFreshStack now944 // to create the root work-in-progress fiber. This is a bit weird in terms945 // of factoring, because it relies on renderRootSync not calling946 // prepareFreshStack again in the call below, which happens because the947 // root and lanes haven't changed.948 //949 // TODO: I think what we should do is set ForceClientRender inside950 // throwException, like we do for nested Suspense boundaries. The reason951 // it's here instead is so we can switch to the synchronous work loop, too.952 // Something to consider for a future refactor.953 const rootWorkInProgress = prepareFreshStack(root, errorRetryLanes);954 rootWorkInProgress.flags |= ForceClientRender;955 if (__DEV__) {956 errorHydratingContainer(root.containerInfo);957 }958 }959 const exitStatus = renderRootSync(root, errorRetryLanes);960 if (exitStatus !== RootErrored) {961 // Successfully finished rendering on retry962 // The errors from the failed first attempt have been recovered. Add963 // them to the collection of recoverable errors. We'll log them in the964 // commit phase.965 const errorsFromSecondAttempt = workInProgressRootRecoverableErrors;966 workInProgressRootRecoverableErrors = errorsFromFirstAttempt;967 // The errors from the second attempt should be queued after the errors968 // from the first attempt, to preserve the causal sequence.969 if (errorsFromSecondAttempt !== null) {970 queueRecoverableErrors(errorsFromSecondAttempt);971 }972 } else {973 // The UI failed to recover.974 }975 return exitStatus;976}977export function queueRecoverableErrors(errors: Array<mixed>) {978 if (workInProgressRootRecoverableErrors === null) {979 workInProgressRootRecoverableErrors = errors;980 } else {981 workInProgressRootRecoverableErrors.push.apply(982 workInProgressRootRecoverableErrors,983 errors,984 );985 }986}987function finishConcurrentRender(root, exitStatus, lanes) {988 switch (exitStatus) {989 case RootInProgress:990 case RootFatalErrored: {991 throw new Error('Root did not complete. This is a bug in React.');992 }993 // Flow knows about invariant, so it complains if I add a break994 // statement, but eslint doesn't know about invariant, so it complains995 // if I do. eslint-disable-next-line no-fallthrough996 case RootErrored: {997 // We should have already attempted to retry this tree. If we reached998 // this point, it errored again. Commit it.999 commitRoot(1000 root,1001 workInProgressRootRecoverableErrors,1002 workInProgressTransitions,1003 );1004 break;1005 }1006 case RootSuspended: {1007 markRootSuspended(root, lanes);1008 // We have an acceptable loading state. We need to figure out if we1009 // should immediately commit it or wait a bit.1010 if (1011 includesOnlyRetries(lanes) &&1012 // do not delay if we're inside an act() scope1013 !shouldForceFlushFallbacksInDEV()1014 ) {1015 // This render only included retries, no updates. Throttle committing1016 // retries so that we don't show too many loading states too quickly.1017 const msUntilTimeout =1018 globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now();1019 // Don't bother with a very short suspense time.1020 if (msUntilTimeout > 10) {1021 const nextLanes = getNextLanes(root, NoLanes);1022 if (nextLanes !== NoLanes) {1023 // There's additional work on this root.1024 break;1025 }1026 const suspendedLanes = root.suspendedLanes;1027 if (!isSubsetOfLanes(suspendedLanes, lanes)) {1028 // We should prefer to render the fallback of at the last1029 // suspended level. Ping the last suspended level to try1030 // rendering it again.1031 // FIXME: What if the suspended lanes are Idle? Should not restart.1032 const eventTime = requestEventTime();1033 markRootPinged(root, suspendedLanes, eventTime);1034 break;1035 }1036 // The render is suspended, it hasn't timed out, and there's no1037 // lower priority work to do. Instead of committing the fallback1038 // immediately, wait for more data to arrive.1039 root.timeoutHandle = scheduleTimeout(1040 commitRoot.bind(1041 null,1042 root,1043 workInProgressRootRecoverableErrors,1044 workInProgressTransitions,1045 ),1046 msUntilTimeout,1047 );1048 break;1049 }1050 }1051 // The work expired. Commit immediately.1052 commitRoot(1053 root,1054 workInProgressRootRecoverableErrors,1055 workInProgressTransitions,1056 );1057 break;1058 }1059 case RootSuspendedWithDelay: {1060 markRootSuspended(root, lanes);1061 if (includesOnlyNonUrgentLanes(lanes)) {1062 // This is a transition, so we should exit without committing a1063 // placeholder and without scheduling a timeout. Delay indefinitely1064 // until we receive more data.1065 break;1066 }1067 if (!shouldForceFlushFallbacksInDEV()) {1068 // This is not a transition, but we did trigger an avoided state.1069 // Schedule a placeholder to display after a short delay, using the Just1070 // Noticeable Difference.1071 // TODO: Is the JND optimization worth the added complexity? If this is1072 // the only reason we track the event time, then probably not.1073 // Consider removing.1074 const mostRecentEventTime = getMostRecentEventTime(root, lanes);1075 const eventTimeMs = mostRecentEventTime;1076 const timeElapsedMs = now() - eventTimeMs;1077 const msUntilTimeout = jnd(timeElapsedMs) - timeElapsedMs;1078 // Don't bother with a very short suspense time.1079 if (msUntilTimeout > 10) {1080 // Instead of committing the fallback immediately, wait for more data1081 // to arrive.1082 root.timeoutHandle = scheduleTimeout(1083 commitRoot.bind(1084 null,1085 root,1086 workInProgressRootRecoverableErrors,1087 workInProgressTransitions,1088 ),1089 msUntilTimeout,1090 );1091 break;1092 }1093 }1094 // Commit the placeholder.1095 commitRoot(1096 root,1097 workInProgressRootRecoverableErrors,1098 workInProgressTransitions,1099 );1100 break;1101 }1102 case RootCompleted: {1103 // The work completed. Ready to commit.1104 commitRoot(1105 root,1106 workInProgressRootRecoverableErrors,1107 workInProgressTransitions,1108 );1109 break;1110 }1111 default: {1112 throw new Error('Unknown root exit status.');1113 }1114 }1115}1116function isRenderConsistentWithExternalStores(finishedWork: Fiber): boolean {1117 // Search the rendered tree for external store reads, and check whether the1118 // stores were mutated in a concurrent event. Intentionally using an iterative1119 // loop instead of recursion so we can exit early.1120 let node: Fiber = finishedWork;1121 while (true) {1122 if (node.flags & StoreConsistency) {1123 const updateQueue: FunctionComponentUpdateQueue | null = (node.updateQueue: any);1124 if (updateQueue !== null) {1125 const checks = updateQueue.stores;1126 if (checks !== null) {1127 for (let i = 0; i < checks.length; i++) {1128 const check = checks[i];1129 const getSnapshot = check.getSnapshot;1130 const renderedValue = check.value;1131 try {1132 if (!is(getSnapshot(), renderedValue)) {1133 // Found an inconsistent store.1134 return false;1135 }1136 } catch (error) {1137 // If `getSnapshot` throws, return `false`. This will schedule1138 // a re-render, and the error will be rethrown during render.1139 return false;1140 }1141 }1142 }1143 }1144 }1145 const child = node.child;1146 if (node.subtreeFlags & StoreConsistency && child !== null) {1147 child.return = node;1148 node = child;1149 continue;1150 }1151 if (node === finishedWork) {1152 return true;1153 }1154 while (node.sibling === null) {1155 if (node.return === null || node.return === finishedWork) {1156 return true;1157 }1158 node = node.return;1159 }1160 node.sibling.return = node.return;1161 node = node.sibling;1162 }1163 // Flow doesn't know this is unreachable, but eslint does1164 // eslint-disable-next-line no-unreachable1165 return true;1166}1167function markRootSuspended(root, suspendedLanes) {1168 // When suspending, we should always exclude lanes that were pinged or (more1169 // rarely, since we try to avoid it) updated during the render phase.1170 // TODO: Lol maybe there's a better way to factor this besides this1171 // obnoxiously named function :)1172 suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);1173 suspendedLanes = removeLanes(1174 suspendedLanes,1175 workInProgressRootInterleavedUpdatedLanes,1176 );1177 markRootSuspended_dontCallThisOneDirectly(root, suspendedLanes);1178}1179// This is the entry point for synchronous tasks that don't go1180// through Scheduler1181function performSyncWorkOnRoot(root) {1182 if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {1183 syncNestedUpdateFlag();1184 }1185 if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {1186 throw new Error('Should not already be working.');1187 }1188 flushPassiveEffects();1189 let lanes = getNextLanes(root, NoLanes);1190 if (!includesSomeLane(lanes, SyncLane)) {1191 // There's no remaining sync work left.1192 ensureRootIsScheduled(root, now());1193 return null;1194 }1195 let exitStatus = renderRootSync(root, lanes);1196 if (root.tag !== LegacyRoot && exitStatus === RootErrored) {1197 // If something threw an error, try rendering one more time. We'll render1198 // synchronously to block concurrent data mutations, and we'll includes1199 // all pending updates are included. If it still fails after the second1200 // attempt, we'll give up and commit the resulting tree.1201 const errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);1202 if (errorRetryLanes !== NoLanes) {1203 lanes = errorRetryLanes;1204 exitStatus = recoverFromConcurrentError(root, errorRetryLanes);1205 }1206 }1207 if (exitStatus === RootFatalErrored) {1208 const fatalError = workInProgressRootFatalError;1209 prepareFreshStack(root, NoLanes);1210 markRootSuspended(root, lanes);1211 ensureRootIsScheduled(root, now());1212 throw fatalError;1213 }1214 if (exitStatus === RootDidNotComplete) {1215 throw new Error('Root did not complete. This is a bug in React.');1216 }1217 // We now have a consistent tree. Because this is a sync render, we1218 // will commit it even if something suspended.1219 const finishedWork: Fiber = (root.current.alternate: any);1220 root.finishedWork = finishedWork;1221 root.finishedLanes = lanes;1222 commitRoot(1223 root,1224 workInProgressRootRecoverableErrors,1225 workInProgressTransitions,1226 );1227 // Before exiting, make sure there's a callback scheduled for the next1228 // pending level.1229 ensureRootIsScheduled(root, now());1230 return null;1231}1232export function flushRoot(root: FiberRoot, lanes: Lanes) {1233 if (lanes !== NoLanes) {1234 markRootEntangled(root, mergeLanes(lanes, SyncLane));1235 ensureRootIsScheduled(root, now());1236 if ((executionContext & (RenderContext | CommitContext)) === NoContext) {1237 resetRenderTimer();1238 flushSyncCallbacks();1239 }1240 }1241}1242export function getExecutionContext(): ExecutionContext {1243 return executionContext;1244}1245export function deferredUpdates<A>(fn: () => A): A {1246 const previousPriority = getCurrentUpdatePriority();1247 const prevTransition = ReactCurrentBatchConfig.transition;1248 try {1249 ReactCurrentBatchConfig.transition = null;1250 setCurrentUpdatePriority(DefaultEventPriority);1251 return fn();1252 } finally {1253 setCurrentUpdatePriority(previousPriority);1254 ReactCurrentBatchConfig.transition = prevTransition;1255 }1256}1257export function batchedUpdates<A, R>(fn: A => R, a: A): R {1258 const prevExecutionContext = executionContext;1259 executionContext |= BatchedContext;1260 try {1261 return fn(a);1262 } finally {1263 executionContext = prevExecutionContext;1264 // If there were legacy sync updates, flush them at the end of the outer1265 // most batchedUpdates-like method.1266 if (1267 executionContext === NoContext &&1268 // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.1269 !(__DEV__ && ReactCurrentActQueue.isBatchingLegacy)1270 ) {1271 resetRenderTimer();1272 flushSyncCallbacksOnlyInLegacyMode();1273 }1274 }1275}1276export function discreteUpdates<A, B, C, D, R>(1277 fn: (A, B, C, D) => R,1278 a: A,1279 b: B,1280 c: C,1281 d: D,1282): R {1283 const previousPriority = getCurrentUpdatePriority();1284 const prevTransition = ReactCurrentBatchConfig.transition;1285 try {1286 ReactCurrentBatchConfig.transition = null;1287 setCurrentUpdatePriority(DiscreteEventPriority);1288 return fn(a, b, c, d);1289 } finally {1290 setCurrentUpdatePriority(previousPriority);1291 ReactCurrentBatchConfig.transition = prevTransition;1292 if (executionContext === NoContext) {1293 resetRenderTimer();1294 }1295 }1296}1297// Overload the definition to the two valid signatures.1298// Warning, this opts-out of checking the function body.1299declare function flushSync<R>(fn: () => R): R;1300// eslint-disable-next-line no-redeclare1301declare function flushSync(): void;1302// eslint-disable-next-line no-redeclare1303export function flushSync(fn) {1304 // In legacy mode, we flush pending passive effects at the beginning of the1305 // next event, not at the end of the previous one.1306 if (1307 rootWithPendingPassiveEffects !== null &&1308 rootWithPendingPassiveEffects.tag === LegacyRoot &&1309 (executionContext & (RenderContext | CommitContext)) === NoContext1310 ) {1311 flushPassiveEffects();1312 }1313 const prevExecutionContext = executionContext;1314 executionContext |= BatchedContext;1315 const prevTransition = ReactCurrentBatchConfig.transition;1316 const previousPriority = getCurrentUpdatePriority();1317 try {1318 ReactCurrentBatchConfig.transition = null;1319 setCurrentUpdatePriority(DiscreteEventPriority);1320 if (fn) {1321 return fn();1322 } else {1323 return undefined;1324 }1325 } finally {1326 setCurrentUpdatePriority(previousPriority);1327 ReactCurrentBatchConfig.transition = prevTransition;1328 executionContext = prevExecutionContext;1329 // Flush the immediate callbacks that were scheduled during this batch.1330 // Note that this will happen even if batchedUpdates is higher up1331 // the stack.1332 if ((executionContext & (RenderContext | CommitContext)) === NoContext) {1333 flushSyncCallbacks();1334 }1335 }1336}1337export function isAlreadyRendering() {1338 // Used by the renderer to print a warning if certain APIs are called from1339 // the wrong context.1340 return (1341 __DEV__ &&1342 (executionContext & (RenderContext | CommitContext)) !== NoContext1343 );1344}1345export function flushControlled(fn: () => mixed): void {1346 const prevExecutionContext = executionContext;1347 executionContext |= BatchedContext;1348 const prevTransition = ReactCurrentBatchConfig.transition;1349 const previousPriority = getCurrentUpdatePriority();1350 try {1351 ReactCurrentBatchConfig.transition = null;1352 setCurrentUpdatePriority(DiscreteEventPriority);1353 fn();1354 } finally {1355 setCurrentUpdatePriority(previousPriority);1356 ReactCurrentBatchConfig.transition = prevTransition;1357 executionContext = prevExecutionContext;1358 if (executionContext === NoContext) {1359 // Flush the immediate callbacks that were scheduled during this batch1360 resetRenderTimer();1361 flushSyncCallbacks();1362 }1363 }1364}1365export function pushRenderLanes(fiber: Fiber, lanes: Lanes) {1366 pushToStack(subtreeRenderLanesCursor, subtreeRenderLanes, fiber);1367 subtreeRenderLanes = mergeLanes(subtreeRenderLanes, lanes);1368 workInProgressRootIncludedLanes = mergeLanes(1369 workInProgressRootIncludedLanes,1370 lanes,1371 );1372}1373export function popRenderLanes(fiber: Fiber) {1374 subtreeRenderLanes = subtreeRenderLanesCursor.current;1375 popFromStack(subtreeRenderLanesCursor, fiber);1376}1377function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber {1378 root.finishedWork = null;1379 root.finishedLanes = NoLanes;1380 const timeoutHandle = root.timeoutHandle;1381 if (timeoutHandle !== noTimeout) {1382 // The root previous suspended and scheduled a timeout to commit a fallback1383 // state. Now that we have additional work, cancel the timeout.1384 root.timeoutHandle = noTimeout;1385 // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above1386 cancelTimeout(timeoutHandle);1387 }1388 if (workInProgress !== null) {1389 let interruptedWork = workInProgress.return;1390 while (interruptedWork !== null) {1391 const current = interruptedWork.alternate;1392 unwindInterruptedWork(1393 current,1394 interruptedWork,1395 workInProgressRootRenderLanes,1396 );1397 interruptedWork = interruptedWork.return;1398 }1399 }1400 workInProgressRoot = root;1401 const rootWorkInProgress = createWorkInProgress(root.current, null);1402 workInProgress = rootWorkInProgress;1403 workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;1404 workInProgressRootExitStatus = RootInProgress;1405 workInProgressRootFatalError = null;1406 workInProgressRootSkippedLanes = NoLanes;1407 workInProgressRootInterleavedUpdatedLanes = NoLanes;1408 workInProgressRootRenderPhaseUpdatedLanes = NoLanes;1409 workInProgressRootPingedLanes = NoLanes;1410 workInProgressRootConcurrentErrors = null;1411 workInProgressRootRecoverableErrors = null;1412 enqueueInterleavedUpdates();1413 if (__DEV__) {1414 ReactStrictModeWarnings.discardPendingWarnings();1415 }1416 return rootWorkInProgress;1417}1418function handleError(root, thrownValue): void {1419 do {1420 let erroredWork = workInProgress;1421 try {1422 // Reset module-level state that was set during the render phase.1423 resetContextDependencies();1424 resetHooksAfterThrow();1425 resetCurrentDebugFiberInDEV();1426 // TODO: I found and added this missing line while investigating a1427 // separate issue. Write a regression test using string refs.1428 ReactCurrentOwner.current = null;1429 if (erroredWork === null || erroredWork.return === null) {1430 // Expected to be working on a non-root fiber. This is a fatal error1431 // because there's no ancestor that can handle it; the root is1432 // supposed to capture all errors that weren't caught by an error1433 // boundary.1434 workInProgressRootExitStatus = RootFatalErrored;1435 workInProgressRootFatalError = thrownValue;1436 // Set `workInProgress` to null. This represents advancing to the next1437 // sibling, or the parent if there are no siblings. But since the root1438 // has no siblings nor a parent, we set it to null. Usually this is1439 // handled by `completeUnitOfWork` or `unwindWork`, but since we're1440 // intentionally not calling those, we need set it here.1441 // TODO: Consider calling `unwindWork` to pop the contexts.1442 workInProgress = null;1443 return;1444 }1445 if (enableProfilerTimer && erroredWork.mode & ProfileMode) {1446 // Record the time spent rendering before an error was thrown. This1447 // avoids inaccurate Profiler durations in the case of a1448 // suspended render.1449 stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true);1450 }1451 if (enableSchedulingProfiler) {1452 markComponentRenderStopped();1453 if (1454 thrownValue !== null &&1455 typeof thrownValue === 'object' &&1456 typeof thrownValue.then === 'function'1457 ) {1458 const wakeable: Wakeable = (thrownValue: any);1459 markComponentSuspended(1460 erroredWork,1461 wakeable,1462 workInProgressRootRenderLanes,1463 );1464 } else {1465 markComponentErrored(1466 erroredWork,1467 thrownValue,1468 workInProgressRootRenderLanes,1469 );1470 }1471 }1472 throwException(1473 root,1474 erroredWork.return,1475 erroredWork,1476 thrownValue,1477 workInProgressRootRenderLanes,1478 );1479 completeUnitOfWork(erroredWork);1480 } catch (yetAnotherThrownValue) {1481 // Something in the return path also threw.1482 thrownValue = yetAnotherThrownValue;1483 if (workInProgress === erroredWork && erroredWork !== null) {1484 // If this boundary has already errored, then we had trouble processing1485 // the error. Bubble it to the next boundary.1486 erroredWork = erroredWork.return;1487 workInProgress = erroredWork;1488 } else {1489 erroredWork = workInProgress;1490 }1491 continue;1492 }1493 // Return to the normal work loop.1494 return;1495 } while (true);1496}1497function pushDispatcher() {1498 const prevDispatcher = ReactCurrentDispatcher.current;1499 ReactCurrentDispatcher.current = ContextOnlyDispatcher;1500 if (prevDispatcher === null) {1501 // The React isomorphic package does not include a default dispatcher.1502 // Instead the first renderer will lazily attach one, in order to give1503 // nicer error messages.1504 return ContextOnlyDispatcher;1505 } else {1506 return prevDispatcher;1507 }1508}1509function popDispatcher(prevDispatcher) {1510 ReactCurrentDispatcher.current = prevDispatcher;1511}1512export function markCommitTimeOfFallback() {1513 globalMostRecentFallbackTime = now();1514}1515export function markSkippedUpdateLanes(lane: Lane | Lanes): void {1516 workInProgressRootSkippedLanes = mergeLanes(1517 lane,1518 workInProgressRootSkippedLanes,1519 );1520}1521export function renderDidSuspend(): void {1522 if (workInProgressRootExitStatus === RootInProgress) {1523 workInProgressRootExitStatus = RootSuspended;1524 }1525}1526export function renderDidSuspendDelayIfPossible(): void {1527 if (1528 workInProgressRootExitStatus === RootInProgress ||1529 workInProgressRootExitStatus === RootSuspended ||1530 workInProgressRootExitStatus === RootErrored1531 ) {1532 workInProgressRootExitStatus = RootSuspendedWithDelay;1533 }1534 // Check if there are updates that we skipped tree that might have unblocked1535 // this render.1536 if (1537 workInProgressRoot !== null &&1538 (includesNonIdleWork(workInProgressRootSkippedLanes) ||1539 includesNonIdleWork(workInProgressRootInterleavedUpdatedLanes))1540 ) {1541 // Mark the current render as suspended so that we switch to working on1542 // the updates that were skipped. Usually we only suspend at the end of1543 // the render phase.1544 // TODO: We should probably always mark the root as suspended immediately1545 // (inside this function), since by suspending at the end of the render1546 // phase introduces a potential mistake where we suspend lanes that were1547 // pinged or updated while we were rendering.1548 markRootSuspended(workInProgressRoot, workInProgressRootRenderLanes);1549 }1550}1551export function renderDidError(error: mixed) {1552 if (workInProgressRootExitStatus !== RootSuspendedWithDelay) {1553 workInProgressRootExitStatus = RootErrored;1554 }1555 if (workInProgressRootConcurrentErrors === null) {1556 workInProgressRootConcurrentErrors = [error];1557 } else {1558 workInProgressRootConcurrentErrors.push(error);1559 }1560}1561// Called during render to determine if anything has suspended.1562// Returns false if we're not sure.1563export function renderHasNotSuspendedYet(): boolean {1564 // If something errored or completed, we can't really be sure,1565 // so those are false.1566 return workInProgressRootExitStatus === RootInProgress;1567}1568function renderRootSync(root: FiberRoot, lanes: Lanes) {1569 const prevExecutionContext = executionContext;1570 executionContext |= RenderContext;1571 const prevDispatcher = pushDispatcher();1572 // If the root or lanes have changed, throw out the existing stack1573 // and prepare a fresh one. Otherwise we'll continue where we left off.1574 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1575 if (enableUpdaterTracking) {1576 if (isDevToolsPresent) {1577 const memoizedUpdaters = root.memoizedUpdaters;1578 if (memoizedUpdaters.size > 0) {1579 restorePendingUpdaters(root, workInProgressRootRenderLanes);1580 memoizedUpdaters.clear();1581 }1582 // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.1583 // If we bailout on this work, we'll move them back (like above).1584 // It's important to move them now in case the work spawns more work at the same priority with different updaters.1585 // That way we can keep the current update and future updates separate.1586 movePendingFibersToMemoized(root, lanes);1587 }1588 }1589 workInProgressTransitions = getTransitionsForLanes(root, lanes);1590 prepareFreshStack(root, lanes);1591 }1592 if (__DEV__) {1593 if (enableDebugTracing) {1594 logRenderStarted(lanes);1595 }1596 }1597 if (enableSchedulingProfiler) {1598 markRenderStarted(lanes);1599 }1600 do {1601 try {1602 workLoopSync();1603 break;1604 } catch (thrownValue) {1605 handleError(root, thrownValue);1606 }1607 } while (true);1608 resetContextDependencies();1609 executionContext = prevExecutionContext;1610 popDispatcher(prevDispatcher);1611 if (workInProgress !== null) {1612 // This is a sync render, so we should have finished the whole tree.1613 throw new Error(1614 'Cannot commit an incomplete root. This error is likely caused by a ' +1615 'bug in React. Please file an issue.',1616 );1617 }1618 if (__DEV__) {1619 if (enableDebugTracing) {1620 logRenderStopped();1621 }1622 }1623 if (enableSchedulingProfiler) {1624 markRenderStopped();1625 }1626 // Set this to null to indicate there's no in-progress render.1627 workInProgressRoot = null;1628 workInProgressRootRenderLanes = NoLanes;1629 return workInProgressRootExitStatus;1630}1631// The work loop is an extremely hot path. Tell Closure not to inline it.1632/** @noinline */1633function workLoopSync() {1634 // Already timed out, so perform work without checking if we need to yield.1635 while (workInProgress !== null) {1636 performUnitOfWork(workInProgress);1637 }1638}1639function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {1640 const prevExecutionContext = executionContext;1641 executionContext |= RenderContext;1642 const prevDispatcher = pushDispatcher();1643 // If the root or lanes have changed, throw out the existing stack1644 // and prepare a fresh one. Otherwise we'll continue where we left off.1645 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1646 if (enableUpdaterTracking) {1647 if (isDevToolsPresent) {1648 const memoizedUpdaters = root.memoizedUpdaters;1649 if (memoizedUpdaters.size > 0) {1650 restorePendingUpdaters(root, workInProgressRootRenderLanes);1651 memoizedUpdaters.clear();1652 }1653 // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.1654 // If we bailout on this work, we'll move them back (like above).1655 // It's important to move them now in case the work spawns more work at the same priority with different updaters.1656 // That way we can keep the current update and future updates separate.1657 movePendingFibersToMemoized(root, lanes);1658 }1659 }1660 workInProgressTransitions = getTransitionsForLanes(root, lanes);1661 resetRenderTimer();1662 prepareFreshStack(root, lanes);1663 }1664 if (__DEV__) {1665 if (enableDebugTracing) {1666 logRenderStarted(lanes);1667 }1668 }1669 if (enableSchedulingProfiler) {1670 markRenderStarted(lanes);1671 }1672 do {1673 try {1674 workLoopConcurrent();1675 break;1676 } catch (thrownValue) {1677 handleError(root, thrownValue);1678 }1679 } while (true);1680 resetContextDependencies();1681 popDispatcher(prevDispatcher);1682 executionContext = prevExecutionContext;1683 if (__DEV__) {1684 if (enableDebugTracing) {1685 logRenderStopped();1686 }1687 }1688 // Check if the tree has completed.1689 if (workInProgress !== null) {1690 // Still work remaining.1691 if (enableSchedulingProfiler) {1692 markRenderYielded();1693 }1694 return RootInProgress;1695 } else {1696 // Completed the tree.1697 if (enableSchedulingProfiler) {1698 markRenderStopped();1699 }1700 // Set this to null to indicate there's no in-progress render.1701 workInProgressRoot = null;1702 workInProgressRootRenderLanes = NoLanes;1703 // Return the final exit status.1704 return workInProgressRootExitStatus;1705 }1706}1707/** @noinline */1708function workLoopConcurrent() {1709 // Perform work until Scheduler asks us to yield1710 while (workInProgress !== null && !shouldYield()) {1711 performUnitOfWork(workInProgress);1712 }1713}1714function performUnitOfWork(unitOfWork: Fiber): void {1715 // The current, flushed, state of this fiber is the alternate. Ideally1716 // nothing should rely on this, but relying on it here means that we don't1717 // need an additional field on the work in progress.1718 const current = unitOfWork.alternate;1719 setCurrentDebugFiberInDEV(unitOfWork);1720 let next;1721 if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {1722 startProfilerTimer(unitOfWork);1723 next = beginWork(current, unitOfWork, subtreeRenderLanes);1724 stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);1725 } else {1726 next = beginWork(current, unitOfWork, subtreeRenderLanes);1727 }1728 resetCurrentDebugFiberInDEV();1729 unitOfWork.memoizedProps = unitOfWork.pendingProps;1730 if (next === null) {1731 // If this doesn't spawn new work, complete the current work.1732 completeUnitOfWork(unitOfWork);1733 } else {1734 workInProgress = next;1735 }1736 ReactCurrentOwner.current = null;1737}1738function completeUnitOfWork(unitOfWork: Fiber): void {1739 // Attempt to complete the current unit of work, then move to the next1740 // sibling. If there are no more siblings, return to the parent fiber.1741 let completedWork = unitOfWork;1742 do {1743 // The current, flushed, state of this fiber is the alternate. Ideally1744 // nothing should rely on this, but relying on it here means that we don't1745 // need an additional field on the work in progress.1746 const current = completedWork.alternate;1747 const returnFiber = completedWork.return;1748 // Check if the work completed or if something threw.1749 if ((completedWork.flags & Incomplete) === NoFlags) {1750 setCurrentDebugFiberInDEV(completedWork);1751 let next;1752 if (1753 !enableProfilerTimer ||1754 (completedWork.mode & ProfileMode) === NoMode1755 ) {1756 next = completeWork(current, completedWork, subtreeRenderLanes);1757 } else {1758 startProfilerTimer(completedWork);1759 next = completeWork(current, completedWork, subtreeRenderLanes);1760 // Update render duration assuming we didn't error.1761 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1762 }1763 resetCurrentDebugFiberInDEV();1764 if (next !== null) {1765 // Completing this fiber spawned new work. Work on that next.1766 workInProgress = next;1767 return;1768 }1769 } else {1770 // This fiber did not complete because something threw. Pop values off1771 // the stack without entering the complete phase. If this is a boundary,1772 // capture values if possible.1773 const next = unwindWork(current, completedWork, subtreeRenderLanes);1774 // Because this fiber did not complete, don't reset its lanes.1775 if (next !== null) {1776 // If completing this work spawned new work, do that next. We'll come1777 // back here again.1778 // Since we're restarting, remove anything that is not a host effect1779 // from the effect tag.1780 next.flags &= HostEffectMask;1781 workInProgress = next;1782 return;1783 }1784 if (1785 enableProfilerTimer &&1786 (completedWork.mode & ProfileMode) !== NoMode1787 ) {1788 // Record the render duration for the fiber that errored.1789 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1790 // Include the time spent working on failed children before continuing.1791 let actualDuration = completedWork.actualDuration;1792 let child = completedWork.child;1793 while (child !== null) {1794 actualDuration += child.actualDuration;1795 child = child.sibling;1796 }1797 completedWork.actualDuration = actualDuration;1798 }1799 if (returnFiber !== null) {1800 // Mark the parent fiber as incomplete and clear its subtree flags.1801 returnFiber.flags |= Incomplete;1802 returnFiber.subtreeFlags = NoFlags;1803 returnFiber.deletions = null;1804 } else {1805 // We've unwound all the way to the root.1806 workInProgressRootExitStatus = RootDidNotComplete;1807 workInProgress = null;1808 return;1809 }1810 }1811 const siblingFiber = completedWork.sibling;1812 if (siblingFiber !== null) {1813 // If there is more work to do in this returnFiber, do that next.1814 workInProgress = siblingFiber;1815 return;1816 }1817 // Otherwise, return to the parent1818 completedWork = returnFiber;1819 // Update the next thing we're working on in case something throws.1820 workInProgress = completedWork;1821 } while (completedWork !== null);1822 // We've reached the root.1823 if (workInProgressRootExitStatus === RootInProgress) {1824 workInProgressRootExitStatus = RootCompleted;1825 }1826}1827function commitRoot(1828 root: FiberRoot,1829 recoverableErrors: null | Array<mixed>,1830 transitions: Array<Transition> | null,1831) {1832 // TODO: This no longer makes any sense. We already wrap the mutation and1833 // layout phases. Should be able to remove.1834 const previousUpdateLanePriority = getCurrentUpdatePriority();1835 const prevTransition = ReactCurrentBatchConfig.transition;1836 try {1837 ReactCurrentBatchConfig.transition = null;1838 setCurrentUpdatePriority(DiscreteEventPriority);1839 commitRootImpl(1840 root,1841 recoverableErrors,1842 transitions,1843 previousUpdateLanePriority,1844 );1845 } finally {1846 ReactCurrentBatchConfig.transition = prevTransition;1847 setCurrentUpdatePriority(previousUpdateLanePriority);1848 }1849 return null;1850}1851function commitRootImpl(1852 root: FiberRoot,1853 recoverableErrors: null | Array<mixed>,1854 transitions: Array<Transition> | null,1855 renderPriorityLevel: EventPriority,1856) {1857 do {1858 // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which1859 // means `flushPassiveEffects` will sometimes result in additional1860 // passive effects. So we need to keep flushing in a loop until there are1861 // no more pending effects.1862 // TODO: Might be better if `flushPassiveEffects` did not automatically1863 // flush synchronous work at the end, to avoid factoring hazards like this.1864 flushPassiveEffects();1865 } while (rootWithPendingPassiveEffects !== null);1866 flushRenderPhaseStrictModeWarningsInDEV();1867 if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {1868 throw new Error('Should not already be working.');1869 }1870 const finishedWork = root.finishedWork;1871 const lanes = root.finishedLanes;1872 if (__DEV__) {1873 if (enableDebugTracing) {1874 logCommitStarted(lanes);1875 }1876 }1877 if (enableSchedulingProfiler) {1878 markCommitStarted(lanes);1879 }1880 if (finishedWork === null) {1881 if (__DEV__) {1882 if (enableDebugTracing) {1883 logCommitStopped();1884 }1885 }1886 if (enableSchedulingProfiler) {1887 markCommitStopped();1888 }1889 return null;1890 } else {1891 if (__DEV__) {1892 if (lanes === NoLanes) {1893 console.error(1894 'root.finishedLanes should not be empty during a commit. This is a ' +1895 'bug in React.',1896 );1897 }1898 }1899 }1900 root.finishedWork = null;1901 root.finishedLanes = NoLanes;1902 if (finishedWork === root.current) {1903 throw new Error(1904 'Cannot commit the same tree as before. This error is likely caused by ' +1905 'a bug in React. Please file an issue.',1906 );1907 }1908 // commitRoot never returns a continuation; it always finishes synchronously.1909 // So we can clear these now to allow a new callback to be scheduled.1910 root.callbackNode = null;1911 root.callbackPriority = NoLane;1912 // Update the first and last pending times on this root. The new first1913 // pending time is whatever is left on the root fiber.1914 let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);1915 markRootFinished(root, remainingLanes);1916 if (root === workInProgressRoot) {1917 // We can reset these now that they are finished.1918 workInProgressRoot = null;1919 workInProgress = null;1920 workInProgressRootRenderLanes = NoLanes;1921 } else {1922 // This indicates that the last root we worked on is not the same one that1923 // we're committing now. This most commonly happens when a suspended root1924 // times out.1925 }1926 // If there are pending passive effects, schedule a callback to process them.1927 // Do this as early as possible, so it is queued before anything else that1928 // might get scheduled in the commit phase. (See #16714.)1929 // TODO: Delete all other places that schedule the passive effect callback1930 // They're redundant.1931 if (1932 (finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||1933 (finishedWork.flags & PassiveMask) !== NoFlags1934 ) {1935 if (!rootDoesHavePassiveEffects) {1936 rootDoesHavePassiveEffects = true;1937 pendingPassiveEffectsRemainingLanes = remainingLanes;1938 // workInProgressTransitions might be overwritten, so we want1939 // to store it in pendingPassiveTransitions until they get processed1940 // We need to pass this through as an argument to commitRoot1941 // because workInProgressTransitions might have changed between1942 // the previous render and commit if we throttle the commit1943 // with setTimeout1944 pendingPassiveTransitions = transitions;1945 scheduleCallback(NormalSchedulerPriority, () => {1946 flushPassiveEffects();1947 // This render triggered passive effects: release the root cache pool1948 // *after* passive effects fire to avoid freeing a cache pool that may1949 // be referenced by a node in the tree (HostRoot, Cache boundary etc)1950 return null;1951 });1952 }1953 }1954 // Check if there are any effects in the whole tree.1955 // TODO: This is left over from the effect list implementation, where we had1956 // to check for the existence of `firstEffect` to satisfy Flow. I think the1957 // only other reason this optimization exists is because it affects profiling.1958 // Reconsider whether this is necessary.1959 const subtreeHasEffects =1960 (finishedWork.subtreeFlags &1961 (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1962 NoFlags;1963 const rootHasEffect =1964 (finishedWork.flags &1965 (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1966 NoFlags;1967 if (subtreeHasEffects || rootHasEffect) {1968 const prevTransition = ReactCurrentBatchConfig.transition;1969 ReactCurrentBatchConfig.transition = null;1970 const previousPriority = getCurrentUpdatePriority();1971 setCurrentUpdatePriority(DiscreteEventPriority);1972 const prevExecutionContext = executionContext;1973 executionContext |= CommitContext;1974 // Reset this to null before calling lifecycles1975 ReactCurrentOwner.current = null;1976 // The commit phase is broken into several sub-phases. We do a separate pass1977 // of the effect list for each phase: all mutation effects come before all1978 // layout effects, and so on.1979 // The first phase a "before mutation" phase. We use this phase to read the1980 // state of the host tree right before we mutate it. This is where1981 // getSnapshotBeforeUpdate is called.1982 const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(1983 root,1984 finishedWork,1985 );1986 if (enableProfilerTimer) {1987 // Mark the current commit time to be shared by all Profilers in this1988 // batch. This enables them to be grouped later.1989 recordCommitTime();1990 }1991 if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {1992 // Track the root here, rather than in commitLayoutEffects(), because of ref setters.1993 // Updates scheduled during ref detachment should also be flagged.1994 rootCommittingMutationOrLayoutEffects = root;1995 }1996 // The next phase is the mutation phase, where we mutate the host tree.1997 commitMutationEffects(root, finishedWork, lanes);1998 if (enableCreateEventHandleAPI) {1999 if (shouldFireAfterActiveInstanceBlur) {2000 afterActiveInstanceBlur();2001 }2002 }2003 resetAfterCommit(root.containerInfo);2004 // The work-in-progress tree is now the current tree. This must come after2005 // the mutation phase, so that the previous tree is still current during2006 // componentWillUnmount, but before the layout phase, so that the finished2007 // work is current during componentDidMount/Update.2008 root.current = finishedWork;2009 // The next phase is the layout phase, where we call effects that read2010 // the host tree after it's been mutated. The idiomatic use case for this is2011 // layout, but class component lifecycles also fire here for legacy reasons.2012 if (__DEV__) {2013 if (enableDebugTracing) {2014 logLayoutEffectsStarted(lanes);2015 }2016 }2017 if (enableSchedulingProfiler) {2018 markLayoutEffectsStarted(lanes);2019 }2020 commitLayoutEffects(finishedWork, root, lanes);2021 if (__DEV__) {2022 if (enableDebugTracing) {2023 logLayoutEffectsStopped();2024 }2025 }2026 if (enableSchedulingProfiler) {2027 markLayoutEffectsStopped();2028 }2029 if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {2030 rootCommittingMutationOrLayoutEffects = null;2031 }2032 // Tell Scheduler to yield at the end of the frame, so the browser has an2033 // opportunity to paint.2034 requestPaint();2035 executionContext = prevExecutionContext;2036 // Reset the priority to the previous non-sync value.2037 setCurrentUpdatePriority(previousPriority);2038 ReactCurrentBatchConfig.transition = prevTransition;2039 } else {2040 // No effects.2041 root.current = finishedWork;2042 // Measure these anyway so the flamegraph explicitly shows that there were2043 // no effects.2044 // TODO: Maybe there's a better way to report this.2045 if (enableProfilerTimer) {2046 recordCommitTime();2047 }2048 }2049 const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;2050 if (rootDoesHavePassiveEffects) {2051 // This commit has passive effects. Stash a reference to them. But don't2052 // schedule a callback until after flushing layout work.2053 rootDoesHavePassiveEffects = false;2054 rootWithPendingPassiveEffects = root;2055 pendingPassiveEffectsLanes = lanes;2056 } else {2057 // There were no passive effects, so we can immediately release the cache2058 // pool for this render.2059 releaseRootPooledCache(root, remainingLanes);2060 if (__DEV__) {2061 nestedPassiveUpdateCount = 0;2062 rootWithPassiveNestedUpdates = null;2063 }2064 }2065 // Read this again, since an effect might have updated it2066 remainingLanes = root.pendingLanes;2067 // Check if there's remaining work on this root2068 // TODO: This is part of the `componentDidCatch` implementation. Its purpose2069 // is to detect whether something might have called setState inside2070 // `componentDidCatch`. The mechanism is known to be flawed because `setState`2071 // inside `componentDidCatch` is itself flawed â that's why we recommend2072 // `getDerivedStateFromError` instead. However, it could be improved by2073 // checking if remainingLanes includes Sync work, instead of whether there's2074 // any work remaining at all (which would also include stuff like Suspense2075 // retries or transitions). It's been like this for a while, though, so fixing2076 // it probably isn't that urgent.2077 if (remainingLanes === NoLanes) {2078 // If there's no remaining work, we can clear the set of already failed2079 // error boundaries.2080 legacyErrorBoundariesThatAlreadyFailed = null;2081 }2082 if (__DEV__ && enableStrictEffects) {2083 if (!rootDidHavePassiveEffects) {2084 commitDoubleInvokeEffectsInDEV(root.current, false);2085 }2086 }2087 onCommitRootDevTools(finishedWork.stateNode, renderPriorityLevel);2088 if (enableUpdaterTracking) {2089 if (isDevToolsPresent) {2090 root.memoizedUpdaters.clear();2091 }2092 }2093 if (__DEV__) {2094 onCommitRootTestSelector();2095 }2096 // Always call this before exiting `commitRoot`, to ensure that any2097 // additional work on this root is scheduled.2098 ensureRootIsScheduled(root, now());2099 if (recoverableErrors !== null) {2100 // There were errors during this render, but recovered from them without2101 // needing to surface it to the UI. We log them here.2102 const onRecoverableError = root.onRecoverableError;2103 for (let i = 0; i < recoverableErrors.length; i++) {2104 const recoverableError = recoverableErrors[i];2105 onRecoverableError(recoverableError);2106 }2107 }2108 if (hasUncaughtError) {2109 hasUncaughtError = false;2110 const error = firstUncaughtError;2111 firstUncaughtError = null;2112 throw error;2113 }2114 // If the passive effects are the result of a discrete render, flush them2115 // synchronously at the end of the current task so that the result is2116 // immediately observable. Otherwise, we assume that they are not2117 // order-dependent and do not need to be observed by external systems, so we2118 // can wait until after paint.2119 // TODO: We can optimize this by not scheduling the callback earlier. Since we2120 // currently schedule the callback in multiple places, will wait until those2121 // are consolidated.2122 if (2123 includesSomeLane(pendingPassiveEffectsLanes, SyncLane) &&2124 root.tag !== LegacyRoot2125 ) {2126 flushPassiveEffects();2127 }2128 // Read this again, since a passive effect might have updated it2129 remainingLanes = root.pendingLanes;2130 if (includesSomeLane(remainingLanes, (SyncLane: Lane))) {2131 if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {2132 markNestedUpdateScheduled();2133 }2134 // Count the number of times the root synchronously re-renders without2135 // finishing. If there are too many, it indicates an infinite update loop.2136 if (root === rootWithNestedUpdates) {2137 nestedUpdateCount++;2138 } else {2139 nestedUpdateCount = 0;2140 rootWithNestedUpdates = root;2141 }2142 } else {2143 nestedUpdateCount = 0;2144 }2145 // If layout work was scheduled, flush it now.2146 flushSyncCallbacks();2147 if (__DEV__) {2148 if (enableDebugTracing) {2149 logCommitStopped();2150 }2151 }2152 if (enableSchedulingProfiler) {2153 markCommitStopped();2154 }2155 return null;2156}2157function releaseRootPooledCache(root: FiberRoot, remainingLanes: Lanes) {2158 if (enableCache) {2159 const pooledCacheLanes = (root.pooledCacheLanes &= remainingLanes);2160 if (pooledCacheLanes === NoLanes) {2161 // None of the remaining work relies on the cache pool. Clear it so2162 // subsequent requests get a new cache2163 const pooledCache = root.pooledCache;2164 if (pooledCache != null) {2165 root.pooledCache = null;2166 releaseCache(pooledCache);2167 }2168 }2169 }2170}2171export function flushPassiveEffects(): boolean {2172 // Returns whether passive effects were flushed.2173 // TODO: Combine this check with the one in flushPassiveEFfectsImpl. We should2174 // probably just combine the two functions. I believe they were only separate2175 // in the first place because we used to wrap it with2176 // `Scheduler.runWithPriority`, which accepts a function. But now we track the2177 // priority within React itself, so we can mutate the variable directly.2178 if (rootWithPendingPassiveEffects !== null) {2179 // Cache the root since rootWithPendingPassiveEffects is cleared in2180 // flushPassiveEffectsImpl2181 const root = rootWithPendingPassiveEffects;2182 // Cache and clear the remaining lanes flag; it must be reset since this2183 // method can be called from various places, not always from commitRoot2184 // where the remaining lanes are known2185 const remainingLanes = pendingPassiveEffectsRemainingLanes;2186 pendingPassiveEffectsRemainingLanes = NoLanes;2187 const renderPriority = lanesToEventPriority(pendingPassiveEffectsLanes);2188 const priority = lowerEventPriority(DefaultEventPriority, renderPriority);2189 const prevTransition = ReactCurrentBatchConfig.transition;2190 const previousPriority = getCurrentUpdatePriority();2191 try {2192 ReactCurrentBatchConfig.transition = null;2193 setCurrentUpdatePriority(priority);2194 return flushPassiveEffectsImpl();2195 } finally {2196 setCurrentUpdatePriority(previousPriority);2197 ReactCurrentBatchConfig.transition = prevTransition;2198 // Once passive effects have run for the tree - giving components a2199 // chance to retain cache instances they use - release the pooled2200 // cache at the root (if there is one)2201 releaseRootPooledCache(root, remainingLanes);2202 }2203 }2204 return false;2205}2206export function enqueuePendingPassiveProfilerEffect(fiber: Fiber): void {2207 if (enableProfilerTimer && enableProfilerCommitHooks) {2208 pendingPassiveProfilerEffects.push(fiber);2209 if (!rootDoesHavePassiveEffects) {2210 rootDoesHavePassiveEffects = true;2211 scheduleCallback(NormalSchedulerPriority, () => {2212 flushPassiveEffects();2213 return null;2214 });2215 }2216 }2217}2218function flushPassiveEffectsImpl() {2219 if (rootWithPendingPassiveEffects === null) {2220 return false;2221 }2222 // Cache and clear the transitions flag2223 const transitions = pendingPassiveTransitions;2224 pendingPassiveTransitions = null;2225 const root = rootWithPendingPassiveEffects;2226 const lanes = pendingPassiveEffectsLanes;2227 rootWithPendingPassiveEffects = null;2228 // TODO: This is sometimes out of sync with rootWithPendingPassiveEffects.2229 // Figure out why and fix it. It's not causing any known issues (probably2230 // because it's only used for profiling), but it's a refactor hazard.2231 pendingPassiveEffectsLanes = NoLanes;2232 if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {2233 throw new Error('Cannot flush passive effects while already rendering.');2234 }2235 if (__DEV__) {2236 isFlushingPassiveEffects = true;2237 didScheduleUpdateDuringPassiveEffects = false;2238 if (enableDebugTracing) {2239 logPassiveEffectsStarted(lanes);2240 }2241 }2242 if (enableSchedulingProfiler) {2243 markPassiveEffectsStarted(lanes);2244 }2245 const prevExecutionContext = executionContext;2246 executionContext |= CommitContext;2247 commitPassiveUnmountEffects(root.current);2248 commitPassiveMountEffects(root, root.current, lanes, transitions);2249 // TODO: Move to commitPassiveMountEffects2250 if (enableProfilerTimer && enableProfilerCommitHooks) {2251 const profilerEffects = pendingPassiveProfilerEffects;2252 pendingPassiveProfilerEffects = [];2253 for (let i = 0; i < profilerEffects.length; i++) {2254 const fiber = ((profilerEffects[i]: any): Fiber);2255 commitPassiveEffectDurations(root, fiber);2256 }2257 }2258 if (__DEV__) {2259 if (enableDebugTracing) {2260 logPassiveEffectsStopped();2261 }2262 }2263 if (enableSchedulingProfiler) {2264 markPassiveEffectsStopped();2265 }2266 if (__DEV__ && enableStrictEffects) {2267 commitDoubleInvokeEffectsInDEV(root.current, true);2268 }2269 executionContext = prevExecutionContext;2270 flushSyncCallbacks();2271 if (enableTransitionTracing) {2272 const prevPendingTransitionCallbacks = currentPendingTransitionCallbacks;2273 const prevRootTransitionCallbacks = root.transitionCallbacks;2274 if (2275 prevPendingTransitionCallbacks !== null &&2276 prevRootTransitionCallbacks !== null2277 ) {2278 // TODO(luna) Refactor this code into the Host Config2279 // TODO(luna) The end time here is not necessarily accurate2280 // because passive effects could be called before paint2281 // (synchronously) or after paint (normally). We need2282 // to come up with a way to get the correct end time for both cases.2283 // One solution is in the host config, if the passive effects2284 // have not yet been run, make a call to flush the passive effects2285 // right after paint.2286 const endTime = now();2287 currentPendingTransitionCallbacks = null;2288 scheduleCallback(IdleSchedulerPriority, () =>2289 processTransitionCallbacks(2290 prevPendingTransitionCallbacks,2291 endTime,2292 prevRootTransitionCallbacks,2293 ),2294 );2295 }2296 }2297 if (__DEV__) {2298 // If additional passive effects were scheduled, increment a counter. If this2299 // exceeds the limit, we'll fire a warning.2300 if (didScheduleUpdateDuringPassiveEffects) {2301 if (root === rootWithPassiveNestedUpdates) {2302 nestedPassiveUpdateCount++;2303 } else {2304 nestedPassiveUpdateCount = 0;2305 rootWithPassiveNestedUpdates = root;2306 }2307 } else {2308 nestedPassiveUpdateCount = 0;2309 }2310 isFlushingPassiveEffects = false;2311 didScheduleUpdateDuringPassiveEffects = false;2312 }2313 // TODO: Move to commitPassiveMountEffects2314 onPostCommitRootDevTools(root);2315 if (enableProfilerTimer && enableProfilerCommitHooks) {2316 const stateNode = root.current.stateNode;2317 stateNode.effectDuration = 0;2318 stateNode.passiveEffectDuration = 0;2319 }2320 return true;2321}2322export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {2323 return (2324 legacyErrorBoundariesThatAlreadyFailed !== null &&2325 legacyErrorBoundariesThatAlreadyFailed.has(instance)2326 );2327}2328export function markLegacyErrorBoundaryAsFailed(instance: mixed) {2329 if (legacyErrorBoundariesThatAlreadyFailed === null) {2330 legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);2331 } else {2332 legacyErrorBoundariesThatAlreadyFailed.add(instance);2333 }2334}2335function prepareToThrowUncaughtError(error: mixed) {2336 if (!hasUncaughtError) {2337 hasUncaughtError = true;2338 firstUncaughtError = error;2339 }2340}2341export const onUncaughtError = prepareToThrowUncaughtError;2342function captureCommitPhaseErrorOnRoot(2343 rootFiber: Fiber,2344 sourceFiber: Fiber,2345 error: mixed,2346) {2347 const errorInfo = createCapturedValue(error, sourceFiber);2348 const update = createRootErrorUpdate(rootFiber, errorInfo, (SyncLane: Lane));2349 enqueueUpdate(rootFiber, update, (SyncLane: Lane));2350 const eventTime = requestEventTime();2351 const root = markUpdateLaneFromFiberToRoot(rootFiber, (SyncLane: Lane));2352 if (root !== null) {2353 markRootUpdated(root, SyncLane, eventTime);2354 ensureRootIsScheduled(root, eventTime);2355 }2356}2357export function captureCommitPhaseError(2358 sourceFiber: Fiber,2359 nearestMountedAncestor: Fiber | null,2360 error: mixed,2361) {2362 if (__DEV__) {2363 reportUncaughtErrorInDEV(error);2364 setIsRunningInsertionEffect(false);2365 }2366 if (sourceFiber.tag === HostRoot) {2367 // Error was thrown at the root. There is no parent, so the root2368 // itself should capture it.2369 captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);2370 return;2371 }2372 let fiber = null;2373 if (skipUnmountedBoundaries) {2374 fiber = nearestMountedAncestor;2375 } else {2376 fiber = sourceFiber.return;2377 }2378 while (fiber !== null) {2379 if (fiber.tag === HostRoot) {2380 captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);2381 return;2382 } else if (fiber.tag === ClassComponent) {2383 const ctor = fiber.type;2384 const instance = fiber.stateNode;2385 if (2386 typeof ctor.getDerivedStateFromError === 'function' ||2387 (typeof instance.componentDidCatch === 'function' &&2388 !isAlreadyFailedLegacyErrorBoundary(instance))2389 ) {2390 const errorInfo = createCapturedValue(error, sourceFiber);2391 const update = createClassErrorUpdate(2392 fiber,2393 errorInfo,2394 (SyncLane: Lane),2395 );2396 enqueueUpdate(fiber, update, (SyncLane: Lane));2397 const eventTime = requestEventTime();2398 const root = markUpdateLaneFromFiberToRoot(fiber, (SyncLane: Lane));2399 if (root !== null) {2400 markRootUpdated(root, SyncLane, eventTime);2401 ensureRootIsScheduled(root, eventTime);2402 }2403 return;2404 }2405 }2406 fiber = fiber.return;2407 }2408 if (__DEV__) {2409 // TODO: Until we re-land skipUnmountedBoundaries (see #20147), this warning2410 // will fire for errors that are thrown by destroy functions inside deleted2411 // trees. What it should instead do is propagate the error to the parent of2412 // the deleted tree. In the meantime, do not add this warning to the2413 // allowlist; this is only for our internal use.2414 console.error(2415 'Internal React error: Attempted to capture a commit phase error ' +2416 'inside a detached tree. This indicates a bug in React. Likely ' +2417 'causes include deleting the same fiber more than once, committing an ' +2418 'already-finished tree, or an inconsistent return pointer.\n\n' +2419 'Error message:\n\n%s',2420 error,2421 );2422 }2423}2424export function pingSuspendedRoot(2425 root: FiberRoot,2426 wakeable: Wakeable,2427 pingedLanes: Lanes,2428) {2429 const pingCache = root.pingCache;2430 if (pingCache !== null) {2431 // The wakeable resolved, so we no longer need to memoize, because it will2432 // never be thrown again.2433 pingCache.delete(wakeable);2434 }2435 const eventTime = requestEventTime();2436 markRootPinged(root, pingedLanes, eventTime);2437 warnIfSuspenseResolutionNotWrappedWithActDEV(root);2438 if (2439 workInProgressRoot === root &&2440 isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)2441 ) {2442 // Received a ping at the same priority level at which we're currently2443 // rendering. We might want to restart this render. This should mirror2444 // the logic of whether or not a root suspends once it completes.2445 // TODO: If we're rendering sync either due to Sync, Batched or expired,2446 // we should probably never restart.2447 // If we're suspended with delay, or if it's a retry, we'll always suspend2448 // so we can always restart.2449 if (2450 workInProgressRootExitStatus === RootSuspendedWithDelay ||2451 (workInProgressRootExitStatus === RootSuspended &&2452 includesOnlyRetries(workInProgressRootRenderLanes) &&2453 now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS)2454 ) {2455 // Restart from the root.2456 prepareFreshStack(root, NoLanes);2457 } else {2458 // Even though we can't restart right now, we might get an2459 // opportunity later. So we mark this render as having a ping.2460 workInProgressRootPingedLanes = mergeLanes(2461 workInProgressRootPingedLanes,2462 pingedLanes,2463 );2464 }2465 }2466 ensureRootIsScheduled(root, eventTime);2467}2468function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {2469 // The boundary fiber (a Suspense component or SuspenseList component)2470 // previously was rendered in its fallback state. One of the promises that...
env.js
Source:env.js
1const fs = require('fs');2const path = require('path');3const paths = require('./paths');4// Make sure that including paths.js after env.js will read .env variables.5delete require.cache[require.resolve('./paths')];6const NODE_ENV = process.env.NODE_ENV;7if (!NODE_ENV) {8 throw new Error(9 'The NODE_ENV environment variable is required but was not specified.'10 );11}12// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use13const dotenvFiles = [14 `${paths.dotenv}.${NODE_ENV}.local`,15 `${paths.dotenv}.${NODE_ENV}`,16 // Don't include `.env.local` for `test` environment17 // since normally you expect tests to produce the same18 // results for everyone19 NODE_ENV !== 'test' && `${paths.dotenv}.local`,20 paths.dotenv,21].filter(Boolean);22// Load environment variables from .env* files. Suppress warnings using silent23// if this file is missing. dotenv will never modify any environment variables24// that have already been set. Variable expansion is supported in .env files.25// https://github.com/motdotla/dotenv26// https://github.com/motdotla/dotenv-expand27dotenvFiles.forEach(dotenvFile => {28 if (fs.existsSync(dotenvFile)) {29 require('dotenv-expand')(30 require('dotenv').config({31 path: dotenvFile,32 })33 );34 }35});36// We support resolving modules according to `NODE_PATH`.37// This lets you use absolute paths in imports inside large monorepos:38// https://github.com/facebook/create-react-app/issues/253.39// It works similar to `NODE_PATH` in Node itself:40// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders41// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.42// Otherwise, we risk importing Node.js core modules into an app instead of webpack shims.43// https://github.com/facebook/create-react-app/issues/1023#issuecomment-26534442144// We also resolve them to make sure all tools using them work consistently.45const appDirectory = fs.realpathSync(process.cwd());46process.env.NODE_PATH = (process.env.NODE_PATH || '')47 .split(path.delimiter)48 .filter(folder => folder && !path.isAbsolute(folder))49 .map(folder => path.resolve(appDirectory, folder))50 .join(path.delimiter);51// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be52// injected into the application via DefinePlugin in webpack configuration.53const REACT_APP = /^REACT_APP_/i;54function getClientEnvironment(publicUrl) {55 const raw = Object.keys(process.env)56 .filter(key => REACT_APP.test(key))57 .reduce(58 (env, key) => {59 env[key] = process.env[key];60 return env;61 },62 {63 // Useful for determining whether weâre running in production mode.64 // Most importantly, it switches React into the correct mode.65 NODE_ENV: process.env.NODE_ENV || 'development',66 // Useful for resolving the correct path to static assets in `public`.67 // For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.68 // This should only be used as an escape hatch. Normally you would put69 // images into the `src` and `import` them in code to get their paths.70 PUBLIC_URL: publicUrl,71 // We support configuring the sockjs pathname during development.72 // These settings let a developer run multiple simultaneous projects.73 // They are used as the connection `hostname`, `pathname` and `port`74 // in webpackHotDevClient. They are used as the `sockHost`, `sockPath`75 // and `sockPort` options in webpack-dev-server.76 WDS_SOCKET_HOST: process.env.WDS_SOCKET_HOST,77 WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH,78 WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT,79 }80 );81 // Stringify all values so we can feed into webpack DefinePlugin82 const stringified = {83 'process.env': Object.keys(raw).reduce((env, key) => {84 env[key] = JSON.stringify(raw[key]);85 return env;86 }, {}),87 "__DEV__": false,88 "__PROFILE__": true,89 "__EXPERIMENTAL__": true,90 "__UMD__": true,91 __NEW_RECONCILER__: true,92 '__LOG_NAMES__': JSON.stringify([93 // 'createRoot',94 // 'ReactDOMRoot',95 // 'createRootImpl',96 // 'createContainer',97 // 'createFiberRoot',98 // 'createHostRootFiber',99 // 'createFiber',100 // 'FiberNode',101 // 'initializeUpdateQueue',102 // 'markContainerAsRoot',103 // 'listenToAllSupportedEvents',104 // 'jsx',105 'render',106 // 'updateContainer',107 // 'enqueueUpdate',108 // 'scheduleUpdateOnFiber',109 // 'ensureRootIsScheduled',110 // 'unstable_scheduleCallback',111 // 'requestHostCallback',112 // 'performWorkUntilDeadline',113 // 'flushWork',114 // 'workLoop',115 // 'performConcurrentWorkOnRoot',116 // 'flushPassiveEffects',117 // 'renderRootConcurrent',118 // 'prepareFreshStack',119 // 'createWorkInProgress',120 // 'createFiber',121 // 'FiberNode',122 // 'performUnitOfWork',123 // 'beginWork',124 // 'setInitialDOMProperties',125 // 'setInitialProperties',126 // 'diffProperties',127 // 'dispatchEvent',128 // 'mountIndeterminateComponent',129 // 'renderWithHooks',130 'useState',131 // 'mountState',132 // 'mountWorkInProgressHook',133 // 'updateHostRoot',134 // 'cloneUpdateQueue',135 // 'processUpdateQueue',136 // 'getStateFromUpdate',137 // 'reconcileChildren',138 // 'reconcileChildFibers',139 // 'reconcileChildrenArray',140 // 'createChild',141 // 'mountChildFibers',142 // 'createFiberFromElement',143 // 'createFiberFromTypeAndProps',144 // 'completeUnitOfWork',145 // 'completeWork',146 // 'commitRootImpl',147 // 'commitBeforeMutationEffects',148 // 'commitBeforeMutationEffectsImpl',149 // 'commitBeforeMutationLifeCycles',150 // 'clearContainer',151 // 'commitMutationEffectsImpl',152 // 'commitPlacement',153 // 'getHostParentFiber',154 // 'getHostSibling',155 // 'insertOrAppendPlacementNodeIntoContainer',156 // 'insertOrAppendPlacementNode',157 // 'trapClickOnNonInteractiveElement',158 // 'resetAfterCommit',159 // 'restoreSelection',160 // 'recursivelyCommitLayoutEffects',161 // 'ensureRootIsScheduled',162 // 'createInstance',163 // 'createElement',164 // 'updateFiberProps',165 // 'bubbleProperties',166 // 'dispatchDiscreteEvent',167 // 'createEventListenerWrapperWithPriority',168 'updateWorkInProgressHook'169 ]),170 };171 return { raw, stringified };172}...
ReactFiberWorkLoop.js
Source:ReactFiberWorkLoop.js
...49export function computeExpirationForFiber(currentTime, fiber) {50}51export function scheduleUpdateOnFiber(fiber, expirationTime) {52 const root = markUpdateTimeFromFiberToRoot(fiber, expirationTime);53 prepareFreshStack(root, expirationTime);54 performSyncWorkOnRoot(root);55}56function prepareFreshStack(root, expirationTime) {57 root.finishedWork = null;58 if (workInProgress !== null) {59 }60 workInProgress = createWorkInProgress(root.current, null);61}62export function unbatchedUpdates(fn, a) {63 try {64 return fn(a);65 } finally {66 }67}68function completeUnitOfWork(unitOfWork) {69 workInProgress = unitOfWork;70 do {...
ReactFiberInterleavedUpdates.new.js
Source:ReactFiberInterleavedUpdates.new.js
1/**2 * Copyright (c) Facebook, Inc. and its affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 *7 * @flow8 */9import type {UpdateQueue as HookQueue} from './ReactFiberHooks.new';10import type {SharedQueue as ClassQueue} from './ReactUpdateQueue.new';11// An array of all update queues that received updates during the current12// render. When this render exits, either because it finishes or because it is13// interrupted, the interleaved updates will be transfered onto the main part14// of the queue.15let interleavedQueues: Array<16 HookQueue<any, any> | ClassQueue<any>,17> | null = null;18/**19 * è¿ä¸ªæ¹æ³è¢«enqueueUpdate使ç¨,å°±æ¯å¤æ 被æéçfiberçsharedä¸æ没æinterleavedçupdate,20 * å¦ææçè¯,å°±ç´æ¥pushè¿æ¥,ä¸åä»»ä½å¤ç.21 *22 * è¿éå°±æ¯å¨ç»ä»¶æ´æ°çè¿ç¨ä¸ç¨æ·åè¿è¡äºæä½,ç¶å就累积å¨è¿é,å½ä¸æ¬¡æ´æ°ç»æä¹å,åæinterleaved23 * mergeå°pendingéé¢å»,è¿æ ·ä¸ä¸æ¬¡pendingä¸å°±æupdateäº.24 * @param queue25 */26export function pushInterleavedQueue(27 queue: HookQueue<any, any> | ClassQueue<any>,28) {29 if (interleavedQueues === null) {30 interleavedQueues = [queue];31 } else {32 interleavedQueues.push(queue);33 }34}35export function enqueueInterleavedUpdates() {36 /**37 * ç´è¯:38 * å°äº¤éçæ´æ°è½¬ç§»å°ä¸»éåãæ¯ä¸ªéåæä¸ä¸ª`pending`å段åä¸ä¸ª`interleaved`å段ãå½å®ä»¬ä¸39 * 为空æ¶ï¼å®ä»¬æå循ç¯é¾æ¥å表ä¸çæåä¸ä¸ªèç¹ãæ们éè¦å°äº¤éçå表追å å°å¾
å®å表çæ«å°¾ï¼å°40 * å®ä»¬è¿æ¥æä¸ä¸ªåä¸ç循ç¯å表ã41 *42 * å¤æä¸æinterleavedQueuesæ¯ä¸æ¯ç©ºç,ä¸æ¯ç©ºå°±å¾ªç¯.å 为ä¸é¢ä¸ä¸ªæ¹æ³å°±æ¯å¨å¾è¿ä¸ªæ°ç»éé¢æå
¥ä¸ä¸ªä¸ªqueue,43 * é¦å
æç¡®çæ¯ interleavedQueues è¿ä¸ªç©ææ¯ä¸ª Array<UpdateQueue>44 * æ以æ¿å°äºä¸ä¸ªä¸ªqueueä¹å,å°±åqueueä¸çinterleaved,è¿ä¸ªæ¯é¾è¡¨ä¸çæåä¸ä¸ª,ç¶åä¸å建ç«å¨45 * lastInterleavedUpdate ä¸çäºnull46 * è¿if å°±æå½å循ç¯å°çqueue置为null,表示å½åçè¿ä¸ªinterleavedææ¸
空äº47 * ç¶ååæ¾queueçpending48 *49 * firstPending -> 2 -> 3 -> lastPending50 * ^---------------------------|51 *52 * firstInterleaved -> 2 -> 3 -> lastInterleaved53 * ^--------------------------------|54 *55 *56 * firstPending -> 2 -> 3 -> lastPending -> firstInterleaved -> 2 -> 3 -> lastInterleaved57 * ^--------------------------------------------------------------------------|58 *59 * æinterleaved æ¼å¨äºpendingä¸60 * æååæpendingæå lastInterleaved61 * wow ....62 *63 * ç¶åè¿ä¸ªæ¹æ³å¨åªé使ç¨äºå¢? prepareFreshStack è¿ä¸ªæ¹æ³,è¿ä¸ªæ¹æ³åååªé使ç¨å¢ èªå·±æå§64 *65 */66 // Transfer the interleaved updates onto the main queue. Each queue has a67 // `pending` field and an `interleaved` field. When they are not null, they68 // point to the last node in a circular linked list. We need to append the69 // interleaved list to the end of the pending list by joining them into a70 // single, circular list.71 if (interleavedQueues !== null) {72 for (let i = 0; i < interleavedQueues.length; i++) {73 const queue = interleavedQueues[i];74 const lastInterleavedUpdate = queue.interleaved;75 if (lastInterleavedUpdate !== null) {76 queue.interleaved = null;77 const firstInterleavedUpdate = lastInterleavedUpdate.next;78 const lastPendingUpdate = queue.pending;79 if (lastPendingUpdate !== null) {80 const firstPendingUpdate = lastPendingUpdate.next;81 lastPendingUpdate.next = (firstInterleavedUpdate: any);82 lastInterleavedUpdate.next = (firstPendingUpdate: any);83 }84 queue.pending = (lastInterleavedUpdate: any);85 }86 }87 interleavedQueues = null;88 }...
FiberWorkLoop.js
Source:FiberWorkLoop.js
...52}53function renderRootSync(root) {54 if (workInProgressRoot !== root) {55 // Mount56 prepareFreshStack(root)57 }58 workLoopSync()59}60function workLoopSync() {61 while(workInProgress !== null) {62 performUnitOfWork(workInProgress)63 }64}65function performUnitOfWork(unitOfWork) {66 const current = workInProgress.alternate67 const next = beginWork(current, unitOfWork)68 unitOfWork.memoizedProps = unitOfWork.pendingProps69 if (next === null) {70 completeUnitOfWork(unitOfWork)71 } else {72 workInProgress = next73 }74}75function completeUnitOfWork(unitOfWork) {76 let completedWork = unitOfWork77 // èªä¸èä¸æ建78 while (completedWork !== null) {79 const current = completedWork.alternate80 const returnFiber = completedWork.return81 completWork(current, completedWork)82 const siblingFiber = completedWork.sibling83 if (siblingFiber !== null) {84 workInProgress = siblingFiber85 return 86 }87 // The last sibling88 completedWork = returnFiber89 workInProgress = returnFiber90 }91}92function completWork(current, workInProgress) {93 const {pendingProps, type} = workInProgress94 switch(workInProgress.tag) {95 case HostRoot: {96 const {containerInfo} = workInProgress.stateNode97 appendChild(containerInfo, workInProgress.child)98 return null;99 }100 case ClassComponent: {101 return null102 }103 case HostComponent: {104 const instance = createInstance(type,workInProgress.pendingProps, workInProgress)105 workInProgress.stateNode = instance106 appendChild(instance, workInProgress.child)107 return null108 }109 case HostText: {110 const text = document.createTextNode(pendingProps + '')111 workInProgress.stateNode = text112 return null113 }114 }115}116function appendChild(parentInstance, firstChild) {117 let node = firstChild118 while (node !== null) {119 parentInstance.appendChild(node.stateNode)120 node = node.sibling121 }122}123function prepareFreshStack(root) {124 root.finishedWork = null125 workInProgressRoot = root126 workInProgress = createWorkInProgress(root.current)127 workInProgressRootExitStatus = RootIncomplete128}129function createInstance(130 type,131 props,132 internalInstanceHandle133) {134 const instance = document.createElement(type)135 // update internal props136 instance[intenralReactPropsKey] = props137 instance[internalInstanceKey] = internalInstanceHandle...
bfsPromise.js
Source:bfsPromise.js
...12function performSyncWorkOnRoot(root) {13 let status = renderRootSync(root)14}15function renderRootSync(root) {16 prepareFreshStack(root)17 do {18 try {19 workLoopSync();20 break;21 } catch (error) {22 handleError(root, error)23 }24 } while (true);25 return 126}27function prepareFreshStack(root) {28 workInProgress = createProgress(root.current)29}30function workLoopSync() {31 if(!workInProgress) {32 performUnitOfWork(workInProgress)33 }34}35function performUnitOfWork(unitWork) {36 const current = unitWork;37 let next;38 next = beginWork(current, unitWork)39 if(next === null) {40 completeUnitOfWork(unitWork)41 }else {...
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 await page.screenshot({ path: 'example.png' });7 await browser.close();8})();9const { prepareFreshStack } = require('playwright');10module.exports = {11 use: {12 viewport: { width: 1280, height: 720 },13 },14 async setup() {15 await prepareFreshStack();16 },17};18const { test, expect } = require('@playwright/test');19test('basic test', async ({ page }) => {20 const title = page.locator('text=Get started');21 await expect(title).toBeVisible();22});23const { prepareFreshStack } = require('playwright');24module.exports = {25 use: {26 viewport: { width: 1280, height: 720 },27 },28 async setup() {29 await prepareFreshStack();30 },31};32const { test, expect } = require('@playwright/test');33test('basic test', async ({ page }) => {34 const title = page.locator('text=Get started');35 await expect(title).toBeVisible();36});37const { prepareFreshStack } = require('playwright');38module.exports = {39 use: {40 viewport: { width: 1280, height: 720 },
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 await page._delegate.prepareFreshStack();7 await page.waitForSelector('text=Getting Started');8 await browser.close();9})();10import { PlaywrightTestConfig } from '@playwright/test';11const config: PlaywrightTestConfig = {12 use: {13 viewport: { width: 1280, height: 720 },14 },15 {16 use: {17 },18 },19};20export default config;
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const context = await browser.newContext();5 await context._browser._defaultContext._prepareFreshStack();6 const page = await context.newPage();7 await browser.close();8})();9const { chromium } = require('playwright');10(async () => {11 const browser = await chromium.launch();12 const context = await browser.newContext();13 await context._browser._defaultContext._prepareFreshStack();14 const page = await context.newPage();15 await browser.close();16})();17const { chromium } = require('playwright');18(async () => {19 const browser = await chromium.launch();20 const context = await browser.newContext();21 await context._browser._defaultContext._prepareFreshStack();22 const page = await context.newPage();23 await browser.close();24})();25const { chromium } = require('playwright');26(async () => {27 const browser = await chromium.launch();28 const context = await browser.newContext();29 await context._browser._defaultContext._prepareFreshStack();30 const page = await context.newPage();31 await browser.close();32})();33const { chromium } = require('playwright');34(async () => {35 const browser = await chromium.launch();36 const context = await browser.newContext();37 await context._browser._defaultContext._prepareFreshStack();38 const page = await context.newPage();39 await browser.close();40})();41const { chromium } = require('playwright');42(async () => {43 const browser = await chromium.launch();44 const context = await browser.newContext();
Using AI Code Generation
1const { prepareFreshStack } = require('playwright/lib/server/browserType');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.screenshot({ path: 'example.png' });8 await browser.close();9 await prepareFreshStack();10 const browser2 = await chromium.launch();11 const context2 = await browser2.newContext();12 const page2 = await context2.newPage();13 await page2.screenshot({ path: 'example2.png' });14 await browser2.close();15})();16const { prepareFreshStack } = require('playwright/lib/server/browserType');17const { chromium } = require('playwright');18(async () => {19 const browser = await chromium.launch();20 const context = await browser.newContext();21 const page = await context.newPage();22 await page.screenshot({ path: 'example.png' });23 await browser.close();24 await prepareFreshStack();25 const browser2 = await chromium.launch();26 const context2 = await browser2.newContext();27 const page2 = await context2.newPage();28 await page2.screenshot({ path: 'example2.png' });29 await browser2.close();30})();31const { prepareFreshStack } = require('playwright/lib/server/browserType');32const { chromium } = require('playwright');33(async () => {34 const browser = await chromium.launch();35 const context = await browser.newContext();36 const page = await context.newPage();37 await page.screenshot({ path: 'example.png' });38 await browser.close();39 await prepareFreshStack();40 const browser2 = await chromium.launch();41 const context2 = await browser2.newContext();42 const page2 = await context2.newPage();43 await page2.screenshot({ path:
Using AI Code Generation
1const { chromium } = require('playwright');2const { prepareFreshStack } = require('playwright/lib/server/browserType');3(async () => {4 const browser = await chromium.launch();5 const context = await prepareFreshStack(browser);6 const page = await context.newPage();7 await page.screenshot({ path: `example.png` });8 await page.close();9 await context.close();10 await browser.close();11})();12{13 "scripts": {14 },15 "dependencies": {16 }17}
Using AI Code Generation
1const { prepareFreshStack } = require('playwright/lib/server/browserContext');2const { chromium } = require('playwright');3(async () => {4 const browser = await chromium.launch();5 const context = await prepareFreshStack(browser);6 const page = await context.newPage();7 await page.screenshot({ path: 'example.png' });8 await browser.close();9})();
Using AI Code Generation
1import { prepareFreshStack } from '@playwright/test/lib/server/stackTrace';2const stackTrace = prepareFreshStack();3console.log(stackTrace);4import { prepareFreshStack } from '@playwright/test/lib/server/stackTrace';5const stackTrace = prepareFreshStack();6console.log(stackTrace);7import { prepareFreshStack } from '@playwright/test/lib/server/stackTrace';8const stackTrace = prepareFreshStack();9console.log(stackTrace);10import { prepareFreshStack } from '@playwright/test/lib/server/stackTrace';11const stackTrace = prepareFreshStack();12console.log(stackTrace);13import { prepareFreshStack } from '@playwright/test/lib/server/stackTrace';14const stackTrace = prepareFreshStack();15console.log(stackTrace);16import { prepareFreshStack } from '@playwright/test/lib/server/stackTrace';17const stackTrace = prepareFreshStack();18console.log(stackTrace);19import { prepareFreshStack } from '@playwright/test/lib/server/stackTrace';20const stackTrace = prepareFreshStack();21console.log(stackTrace);22import { prepareFreshStack } from '@playwright/test/lib/server/stackTrace';23const stackTrace = prepareFreshStack();24console.log(stackTrace);25import { prepareFreshStack } from '@playwright/test/lib/server/stackTrace';26const stackTrace = prepareFreshStack();27console.log(stackTrace);28import { prepareFreshStack } from '@playwright/test/lib/server/stackTrace';29const stackTrace = prepareFreshStack();30console.log(stackTrace);31import { prepareFreshStack } from '@playwright/test/lib/server/stackTrace';32const stackTrace = prepareFreshStack();33console.log(stackTrace);34import { prepareFreshStack } from '@playwright/test/lib/server/stackTrace';35const stackTrace = prepareFreshStack();
Using AI Code Generation
1const { context } = await prepareFreshStack();2await context.newPage();3await cleanupStack();4const { context } = await prepareFreshStack();5await context.newPage();6await cleanupStack();7const { context } = await prepareFreshStack();8await context.newPage();9await cleanupStack();10const { context } = await prepareFreshStack();11await context.newPage();12await cleanupStack();13const { context } = await prepareFreshStack();14await context.newPage();
LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!