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