How to use prepareFreshStack method in Playwright Internal

Best JavaScript code snippet using playwright-internal

ReactFiberWorkLoop.new.js

Source:ReactFiberWorkLoop.new.js Github

copy

Full Screen

...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)...

Full Screen

Full Screen

ReactFiberWorkLoop.old.js

Source:ReactFiberWorkLoop.old.js Github

copy

Full Screen

...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...

Full Screen

Full Screen

env.js

Source:env.js Github

copy

Full Screen

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}...

Full Screen

Full Screen

ReactFiberWorkLoop.js

Source:ReactFiberWorkLoop.js Github

copy

Full Screen

...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 {...

Full Screen

Full Screen

ReactFiberInterleavedUpdates.new.js

Source:ReactFiberInterleavedUpdates.new.js Github

copy

Full Screen

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 }...

Full Screen

Full Screen

FiberWorkLoop.js

Source:FiberWorkLoop.js Github

copy

Full Screen

...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...

Full Screen

Full Screen

bfsPromise.js

Source:bfsPromise.js Github

copy

Full Screen

...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 {...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

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 },

Full Screen

Using AI Code Generation

copy

Full Screen

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;

Full Screen

Using AI Code Generation

copy

Full Screen

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();

Full Screen

Using AI Code Generation

copy

Full Screen

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:

Full Screen

Using AI Code Generation

copy

Full Screen

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}

Full Screen

Using AI Code Generation

copy

Full Screen

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})();

Full Screen

Using AI Code Generation

copy

Full Screen

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();

Full Screen

Using AI Code Generation

copy

Full Screen

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();

Full Screen

Playwright tutorial

LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.

Chapters:

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

Run Playwright Internal automation tests on LambdaTest cloud grid

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

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful