Best JavaScript code snippet using playwright-internal
ReactFiberWorkLoop.js
Source:ReactFiberWorkLoop.js
...1176 do {1177 try {1178 // Reset module-level state that was set during the render phase.1179 resetContextDependencies();1180 resetHooksAfterThrow();1181 resetCurrentDebugFiberInDEV();1182 if (workInProgress === null || workInProgress.return === null) {1183 // Expected to be working on a non-root fiber. This is a fatal error1184 // because there's no ancestor that can handle it; the root is1185 // supposed to capture all errors that weren't caught by an error1186 // boundary.1187 workInProgressRootExitStatus = RootFatalErrored;1188 workInProgressRootFatalError = thrownValue;1189 // Set `workInProgress` to null. This represents advancing to the next1190 // sibling, or the parent if there are no siblings. But since the root1191 // has no siblings nor a parent, we set it to null. Usually this is1192 // handled by `completeUnitOfWork` or `unwindWork`, but since we're1193 // interntionally not calling those, we need set it here.1194 // TODO: Consider calling `unwindWork` to pop the contexts.1195 workInProgress = null;1196 return null;1197 }1198 if (enableProfilerTimer && workInProgress.mode & ProfileMode) {1199 // Record the time spent rendering before an error was thrown. This1200 // avoids inaccurate Profiler durations in the case of a1201 // suspended render.1202 stopProfilerTimerIfRunningAndRecordDelta(workInProgress, true);1203 }1204 throwException(1205 root,1206 workInProgress.return,1207 workInProgress,1208 thrownValue,1209 renderExpirationTime,1210 );1211 workInProgress = completeUnitOfWork(workInProgress);1212 } catch (yetAnotherThrownValue) {1213 // Something in the return path also threw.1214 thrownValue = yetAnotherThrownValue;1215 continue;1216 }1217 // Return to the normal work loop.1218 return;1219 } while (true);1220}1221function pushDispatcher(root) {1222 const prevDispatcher = ReactCurrentDispatcher.current;1223 ReactCurrentDispatcher.current = ContextOnlyDispatcher;1224 if (prevDispatcher === null) {1225 // The React isomorphic package does not include a default dispatcher.1226 // Instead the first renderer will lazily attach one, in order to give1227 // nicer error messages.1228 return ContextOnlyDispatcher;1229 } else {1230 return prevDispatcher;1231 }1232}1233function popDispatcher(prevDispatcher) {1234 ReactCurrentDispatcher.current = prevDispatcher;1235}1236function pushInteractions(root) {1237 if (enableSchedulerTracing) {1238 const prevInteractions: Set<Interaction> | null = __interactionsRef.current;1239 __interactionsRef.current = root.memoizedInteractions;1240 return prevInteractions;1241 }1242 return null;1243}1244function popInteractions(prevInteractions) {1245 if (enableSchedulerTracing) {1246 __interactionsRef.current = prevInteractions;1247 }1248}1249export function markCommitTimeOfFallback() {1250 globalMostRecentFallbackTime = now();1251}1252export function markRenderEventTimeAndConfig(1253 expirationTime: ExpirationTime,1254 suspenseConfig: null | SuspenseConfig,1255): void {1256 if (1257 expirationTime < workInProgressRootLatestProcessedExpirationTime &&1258 expirationTime > Idle1259 ) {1260 workInProgressRootLatestProcessedExpirationTime = expirationTime;1261 }1262 if (suspenseConfig !== null) {1263 if (1264 expirationTime < workInProgressRootLatestSuspenseTimeout &&1265 expirationTime > Idle1266 ) {1267 workInProgressRootLatestSuspenseTimeout = expirationTime;1268 // Most of the time we only have one config and getting wrong is not bad.1269 workInProgressRootCanSuspendUsingConfig = suspenseConfig;1270 }1271 }1272}1273export function markUnprocessedUpdateTime(1274 expirationTime: ExpirationTime,1275): void {1276 if (expirationTime > workInProgressRootNextUnprocessedUpdateTime) {1277 workInProgressRootNextUnprocessedUpdateTime = expirationTime;1278 }1279}1280export function renderDidSuspend(): void {1281 if (workInProgressRootExitStatus === RootIncomplete) {1282 workInProgressRootExitStatus = RootSuspended;1283 }1284}1285export function renderDidSuspendDelayIfPossible(): void {1286 if (1287 workInProgressRootExitStatus === RootIncomplete ||1288 workInProgressRootExitStatus === RootSuspended1289 ) {1290 workInProgressRootExitStatus = RootSuspendedWithDelay;1291 }1292 // Check if there's a lower priority update somewhere else in the tree.1293 if (1294 workInProgressRootNextUnprocessedUpdateTime !== NoWork &&1295 workInProgressRoot !== null1296 ) {1297 // Mark the current render as suspended, and then mark that there's a1298 // pending update.1299 // TODO: This should immediately interrupt the current render, instead1300 // of waiting until the next time we yield.1301 markRootSuspendedAtTime(workInProgressRoot, renderExpirationTime);1302 markRootUpdatedAtTime(1303 workInProgressRoot,1304 workInProgressRootNextUnprocessedUpdateTime,1305 );1306 }1307}1308export function renderDidError() {1309 if (workInProgressRootExitStatus !== RootCompleted) {1310 workInProgressRootExitStatus = RootErrored;1311 }1312}1313// Called during render to determine if anything has suspended.1314// Returns false if we're not sure.1315export function renderHasNotSuspendedYet(): boolean {1316 // If something errored or completed, we can't really be sure,1317 // so those are false.1318 return workInProgressRootExitStatus === RootIncomplete;1319}1320function inferTimeFromExpirationTime(expirationTime: ExpirationTime): number {1321 // We don't know exactly when the update was scheduled, but we can infer an1322 // approximate start time from the expiration time.1323 const earliestExpirationTimeMs = expirationTimeToMs(expirationTime);1324 return earliestExpirationTimeMs - LOW_PRIORITY_EXPIRATION;1325}1326function inferTimeFromExpirationTimeWithSuspenseConfig(1327 expirationTime: ExpirationTime,1328 suspenseConfig: SuspenseConfig,1329): number {1330 // We don't know exactly when the update was scheduled, but we can infer an1331 // approximate start time from the expiration time by subtracting the timeout1332 // that was added to the event time.1333 const earliestExpirationTimeMs = expirationTimeToMs(expirationTime);1334 return (1335 earliestExpirationTimeMs -1336 (suspenseConfig.timeoutMs | 0 || LOW_PRIORITY_EXPIRATION)1337 );1338}1339// The work loop is an extremely hot path. Tell Closure not to inline it.1340/** @noinline */1341function workLoopSync() {1342 // Already timed out, so perform work without checking if we need to yield.1343 while (workInProgress !== null) {1344 debugger1345 workInProgress = performUnitOfWork(workInProgress);1346 }1347}1348/** @noinline */1349function workLoopConcurrent() {1350 // Perform work until Scheduler asks us to yield1351 while (workInProgress !== null && !shouldYield()) {1352 workInProgress = performUnitOfWork(workInProgress);1353 }1354}1355function performUnitOfWork(unitOfWork: Fiber): Fiber | null {1356 // The current, flushed, state of this fiber is the alternate. Ideally1357 // nothing should rely on this, but relying on it here means that we don't1358 // need an additional field on the work in progress.1359 const current = unitOfWork.alternate;1360 1361 startWorkTimer(unitOfWork);1362 setCurrentDebugFiberInDEV(unitOfWork);1363 let next;1364 if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {1365 startProfilerTimer(unitOfWork);1366 debugger;1367 next = beginWork(current, unitOfWork, renderExpirationTime);1368 stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);1369 } else {1370 next = beginWork(current, unitOfWork, renderExpirationTime);1371 }1372 resetCurrentDebugFiberInDEV();1373 unitOfWork.memoizedProps = unitOfWork.pendingProps;1374 if (next === null) {1375 // If this doesn't spawn new work, complete the current work.1376 next = completeUnitOfWork(unitOfWork);1377 }1378 console.log(next)1379 ReactCurrentOwner.current = null;1380 return next;1381}1382function completeUnitOfWork(unitOfWork: Fiber): Fiber | null {1383 // Attempt to complete the current unit of work, then move to the next1384 // sibling. If there are no more siblings, return to the parent fiber.1385 workInProgress = unitOfWork;1386 do {1387 // The current, flushed, state of this fiber is the alternate. Ideally1388 // nothing should rely on this, but relying on it here means that we don't1389 // need an additional field on the work in progress.1390 const current = workInProgress.alternate;1391 const returnFiber = workInProgress.return;1392 // Check if the work completed or if something threw.1393 if ((workInProgress.effectTag & Incomplete) === NoEffect) {1394 setCurrentDebugFiberInDEV(workInProgress);1395 let next;1396 if (1397 !enableProfilerTimer ||1398 (workInProgress.mode & ProfileMode) === NoMode1399 ) {1400 next = completeWork(current, workInProgress, renderExpirationTime);1401 } else {1402 startProfilerTimer(workInProgress);1403 next = completeWork(current, workInProgress, renderExpirationTime);1404 // Update render duration assuming we didn't error.1405 stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);1406 }1407 stopWorkTimer(workInProgress);1408 resetCurrentDebugFiberInDEV();1409 resetChildExpirationTime(workInProgress);1410 if (next !== null) {1411 // Completing this fiber spawned new work. Work on that next.1412 return next;1413 }1414 if (1415 returnFiber !== null &&1416 // Do not append effects to parents if a sibling failed to complete1417 (returnFiber.effectTag & Incomplete) === NoEffect1418 ) {1419 // Append all the effects of the subtree and this fiber onto the effect1420 // list of the parent. The completion order of the children affects the1421 // side-effect order.1422 if (returnFiber.firstEffect === null) {1423 returnFiber.firstEffect = workInProgress.firstEffect;1424 }1425 if (workInProgress.lastEffect !== null) {1426 if (returnFiber.lastEffect !== null) {1427 returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;1428 }1429 returnFiber.lastEffect = workInProgress.lastEffect;1430 }1431 // If this fiber had side-effects, we append it AFTER the children's1432 // side-effects. We can perform certain side-effects earlier if needed,1433 // by doing multiple passes over the effect list. We don't want to1434 // schedule our own side-effect on our own list because if end up1435 // reusing children we'll schedule this effect onto itself since we're1436 // at the end.1437 const effectTag = workInProgress.effectTag;1438 // Skip both NoWork and PerformedWork tags when creating the effect1439 // list. PerformedWork effect is read by React DevTools but shouldn't be1440 // committed.1441 if (effectTag > PerformedWork) {1442 if (returnFiber.lastEffect !== null) {1443 returnFiber.lastEffect.nextEffect = workInProgress;1444 } else {1445 returnFiber.firstEffect = workInProgress;1446 }1447 returnFiber.lastEffect = workInProgress;1448 }1449 }1450 } else {1451 // This fiber did not complete because something threw. Pop values off1452 // the stack without entering the complete phase. If this is a boundary,1453 // capture values if possible.1454 const next = unwindWork(workInProgress, renderExpirationTime);1455 // Because this fiber did not complete, don't reset its expiration time.1456 if (1457 enableProfilerTimer &&1458 (workInProgress.mode & ProfileMode) !== NoMode1459 ) {1460 // Record the render duration for the fiber that errored.1461 stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);1462 // Include the time spent working on failed children before continuing.1463 let actualDuration = workInProgress.actualDuration;1464 let child = workInProgress.child;1465 while (child !== null) {1466 actualDuration += child.actualDuration;1467 child = child.sibling;1468 }1469 workInProgress.actualDuration = actualDuration;1470 }1471 if (next !== null) {1472 // If completing this work spawned new work, do that next. We'll come1473 // back here again.1474 // Since we're restarting, remove anything that is not a host effect1475 // from the effect tag.1476 // TODO: The name stopFailedWorkTimer is misleading because Suspense1477 // also captures and restarts.1478 stopFailedWorkTimer(workInProgress);1479 next.effectTag &= HostEffectMask;1480 return next;1481 }1482 stopWorkTimer(workInProgress);1483 if (returnFiber !== null) {1484 // Mark the parent fiber as incomplete and clear its effect list.1485 returnFiber.firstEffect = returnFiber.lastEffect = null;1486 returnFiber.effectTag |= Incomplete;1487 }1488 }1489 const siblingFiber = workInProgress.sibling;1490 if (siblingFiber !== null) {1491 // If there is more work to do in this returnFiber, do that next.1492 return siblingFiber;1493 }1494 // Otherwise, return to the parent1495 workInProgress = returnFiber;1496 } while (workInProgress !== null);1497 // We've reached the root.1498 if (workInProgressRootExitStatus === RootIncomplete) {1499 workInProgressRootExitStatus = RootCompleted;1500 }1501 return null;1502}1503function getRemainingExpirationTime(fiber: Fiber) {1504 const updateExpirationTime = fiber.expirationTime;1505 const childExpirationTime = fiber.childExpirationTime;1506 return updateExpirationTime > childExpirationTime1507 ? updateExpirationTime1508 : childExpirationTime;1509}1510function resetChildExpirationTime(completedWork: Fiber) {1511 if (1512 renderExpirationTime !== Never &&1513 completedWork.childExpirationTime === Never1514 ) {1515 // The children of this component are hidden. Don't bubble their1516 // expiration times.1517 return;1518 }1519 let newChildExpirationTime = NoWork;1520 // Bubble up the earliest expiration time.1521 if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {1522 // In profiling mode, resetChildExpirationTime is also used to reset1523 // profiler durations.1524 let actualDuration = completedWork.actualDuration;1525 let treeBaseDuration = completedWork.selfBaseDuration;1526 // When a fiber is cloned, its actualDuration is reset to 0. This value will1527 // only be updated if work is done on the fiber (i.e. it doesn't bailout).1528 // When work is done, it should bubble to the parent's actualDuration. If1529 // the fiber has not been cloned though, (meaning no work was done), then1530 // this value will reflect the amount of time spent working on a previous1531 // render. In that case it should not bubble. We determine whether it was1532 // cloned by comparing the child pointer.1533 const shouldBubbleActualDurations =1534 completedWork.alternate === null ||1535 completedWork.child !== completedWork.alternate.child;1536 let child = completedWork.child;1537 while (child !== null) {1538 const childUpdateExpirationTime = child.expirationTime;1539 const childChildExpirationTime = child.childExpirationTime;1540 if (childUpdateExpirationTime > newChildExpirationTime) {1541 newChildExpirationTime = childUpdateExpirationTime;1542 }1543 if (childChildExpirationTime > newChildExpirationTime) {1544 newChildExpirationTime = childChildExpirationTime;1545 }1546 if (shouldBubbleActualDurations) {1547 actualDuration += child.actualDuration;1548 }1549 treeBaseDuration += child.treeBaseDuration;1550 child = child.sibling;1551 }1552 completedWork.actualDuration = actualDuration;1553 completedWork.treeBaseDuration = treeBaseDuration;1554 } else {1555 let child = completedWork.child;1556 while (child !== null) {1557 const childUpdateExpirationTime = child.expirationTime;1558 const childChildExpirationTime = child.childExpirationTime;1559 if (childUpdateExpirationTime > newChildExpirationTime) {1560 newChildExpirationTime = childUpdateExpirationTime;1561 }1562 if (childChildExpirationTime > newChildExpirationTime) {1563 newChildExpirationTime = childChildExpirationTime;1564 }1565 child = child.sibling;1566 }1567 }1568 completedWork.childExpirationTime = newChildExpirationTime;1569}1570function commitRoot(root) {1571 const renderPriorityLevel = getCurrentPriorityLevel();1572 runWithPriority(1573 ImmediatePriority,1574 commitRootImpl.bind(null, root, renderPriorityLevel),1575 );1576 return null;1577}1578function commitRootImpl(root, renderPriorityLevel) {1579 do {1580 // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which1581 // means `flushPassiveEffects` will sometimes result in additional1582 // passive effects. So we need to keep flushing in a loop until there are1583 // no more pending effects.1584 // TODO: Might be better if `flushPassiveEffects` did not automatically1585 // flush synchronous work at the end, to avoid factoring hazards like this.1586 debugger1587 flushPassiveEffects();1588 } while (rootWithPendingPassiveEffects !== null);1589 flushRenderPhaseStrictModeWarningsInDEV();1590 invariant(1591 (executionContext & (RenderContext | CommitContext)) === NoContext,1592 'Should not already be working.',1593 );1594 const finishedWork = root.finishedWork;1595 const expirationTime = root.finishedExpirationTime;1596 if (finishedWork === null) {1597 return null;1598 }1599 root.finishedWork = null;1600 root.finishedExpirationTime = NoWork;1601 invariant(1602 finishedWork !== root.current,1603 'Cannot commit the same tree as before. This error is likely caused by ' +1604 'a bug in React. Please file an issue.',1605 );1606 // commitRoot never returns a continuation; it always finishes synchronously.1607 // So we can clear these now to allow a new callback to be scheduled.1608 root.callbackNode = null;1609 root.callbackExpirationTime = NoWork;1610 root.callbackPriority = NoPriority;1611 root.nextKnownPendingLevel = NoWork;1612 startCommitTimer();1613 // Update the first and last pending times on this root. The new first1614 // pending time is whatever is left on the root fiber.1615 const remainingExpirationTimeBeforeCommit = getRemainingExpirationTime(1616 finishedWork,1617 );1618 markRootFinishedAtTime(1619 root,1620 expirationTime,1621 remainingExpirationTimeBeforeCommit,1622 );1623 if (root === workInProgressRoot) {1624 // We can reset these now that they are finished.1625 workInProgressRoot = null;1626 workInProgress = null;1627 renderExpirationTime = NoWork;1628 } else {1629 // This indicates that the last root we worked on is not the same one that1630 // we're committing now. This most commonly happens when a suspended root1631 // times out.1632 }1633 // Get the list of effects.1634 let firstEffect;1635 if (finishedWork.effectTag > PerformedWork) {1636 // A fiber's effect list consists only of its children, not itself. So if1637 // the root has an effect, we need to add it to the end of the list. The1638 // resulting list is the set that would belong to the root's parent, if it1639 // had one; that is, all the effects in the tree including the root.1640 if (finishedWork.lastEffect !== null) {1641 finishedWork.lastEffect.nextEffect = finishedWork;1642 firstEffect = finishedWork.firstEffect;1643 } else {1644 firstEffect = finishedWork;1645 }1646 } else {1647 // There is no effect on the root.1648 firstEffect = finishedWork.firstEffect;1649 }1650 if (firstEffect !== null) { // èµ°è¿é1651 const prevExecutionContext = executionContext;1652 executionContext |= CommitContext;1653 const prevInteractions = pushInteractions(root);1654 // Reset this to null before calling lifecycles1655 ReactCurrentOwner.current = null;1656 // The commit phase is broken into several sub-phases. We do a separate pass1657 // of the effect list for each phase: all mutation effects come before all1658 // layout effects, and so on.1659 // The first phase a "before mutation" phase. We use this phase to read the1660 // state of the host tree right before we mutate it. This is where1661 // getSnapshotBeforeUpdate is called.1662 startCommitSnapshotEffectsTimer();1663 prepareForCommit(root.containerInfo);1664 nextEffect = firstEffect;1665 do {1666 if (false) {1667 1668 } else {1669 try {1670 debugger1671 commitBeforeMutationEffects();1672 } catch (error) {1673 invariant(nextEffect !== null, 'Should be working on an effect.');1674 captureCommitPhaseError(nextEffect, error);1675 nextEffect = nextEffect.nextEffect;1676 }1677 }1678 } while (nextEffect !== null);1679 stopCommitSnapshotEffectsTimer();1680 if (enableProfilerTimer) {1681 // Mark the current commit time to be shared by all Profilers in this1682 // batch. This enables them to be grouped later.1683 recordCommitTime();1684 }1685 // The next phase is the mutation phase, where we mutate the host tree.1686 startCommitHostEffectsTimer();1687 nextEffect = firstEffect;1688 do {1689 if (false) {1690 1691 } else {1692 try {1693 debugger1694 commitMutationEffects(root, renderPriorityLevel);1695 } catch (error) {1696 invariant(nextEffect !== null, 'Should be working on an effect.');1697 captureCommitPhaseError(nextEffect, error);1698 nextEffect = nextEffect.nextEffect;1699 }1700 }1701 } while (nextEffect !== null);1702 stopCommitHostEffectsTimer();1703 resetAfterCommit(root.containerInfo);1704 // The work-in-progress tree is now the current tree. This must come after1705 // the mutation phase, so that the previous tree is still current during1706 // componentWillUnmount, but before the layout phase, so that the finished1707 // work is current during componentDidMount/Update.1708 root.current = finishedWork;1709 // The next phase is the layout phase, where we call effects that read1710 // the host tree after it's been mutated. The idiomatic use case for this is1711 // layout, but class component lifecycles also fire here for legacy reasons.1712 startCommitLifeCyclesTimer();1713 nextEffect = firstEffect;1714 do {1715 if (false) {1716 1717 } else {1718 try {1719 debugger1720 commitLayoutEffects(root, expirationTime);1721 } catch (error) {1722 invariant(nextEffect !== null, 'Should be working on an effect.');1723 captureCommitPhaseError(nextEffect, error);1724 nextEffect = nextEffect.nextEffect;1725 }1726 }1727 } while (nextEffect !== null);1728 stopCommitLifeCyclesTimer();1729 nextEffect = null;1730 // Tell Scheduler to yield at the end of the frame, so the browser has an1731 // opportunity to paint.1732 requestPaint();1733 if (enableSchedulerTracing) {1734 popInteractions(((prevInteractions: any): Set<Interaction>));1735 }1736 executionContext = prevExecutionContext;1737 } else {1738 // No effects.1739 root.current = finishedWork;1740 // Measure these anyway so the flamegraph explicitly shows that there were1741 // no effects.1742 // TODO: Maybe there's a better way to report this.1743 startCommitSnapshotEffectsTimer();1744 stopCommitSnapshotEffectsTimer();1745 if (enableProfilerTimer) {1746 recordCommitTime();1747 }1748 startCommitHostEffectsTimer();1749 stopCommitHostEffectsTimer();1750 startCommitLifeCyclesTimer();1751 stopCommitLifeCyclesTimer();1752 }1753 stopCommitTimer();1754 const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;1755 if (rootDoesHavePassiveEffects) {1756 // This commit has passive effects. Stash a reference to them. But don't1757 // schedule a callback until after flushing layout work.1758 rootDoesHavePassiveEffects = false;1759 rootWithPendingPassiveEffects = root;1760 pendingPassiveEffectsExpirationTime = expirationTime;1761 pendingPassiveEffectsRenderPriority = renderPriorityLevel;1762 } else {1763 // We are done with the effect chain at this point so let's clear the1764 // nextEffect pointers to assist with GC. If we have passive effects, we'll1765 // clear this in flushPassiveEffects.1766 nextEffect = firstEffect;1767 while (nextEffect !== null) {1768 const nextNextEffect = nextEffect.nextEffect;1769 nextEffect.nextEffect = null;1770 nextEffect = nextNextEffect;1771 }1772 }1773 // Check if there's remaining work on this root1774 const remainingExpirationTime = root.firstPendingTime;1775 if (remainingExpirationTime !== NoWork) {1776 if (enableSchedulerTracing) {1777 if (spawnedWorkDuringRender !== null) {1778 const expirationTimes = spawnedWorkDuringRender;1779 spawnedWorkDuringRender = null;1780 for (let i = 0; i < expirationTimes.length; i++) {1781 scheduleInteractions(1782 root,1783 expirationTimes[i],1784 root.memoizedInteractions,1785 );1786 }1787 }1788 schedulePendingInteractions(root, remainingExpirationTime);1789 }1790 } else {1791 // If there's no remaining work, we can clear the set of already failed1792 // error boundaries.1793 legacyErrorBoundariesThatAlreadyFailed = null;1794 }1795 if (enableSchedulerTracing) {1796 if (!rootDidHavePassiveEffects) {1797 // If there are no passive effects, then we can complete the pending interactions.1798 // Otherwise, we'll wait until after the passive effects are flushed.1799 // Wait to do this until after remaining work has been scheduled,1800 // so that we don't prematurely signal complete for interactions when there's e.g. hidden work.1801 finishPendingInteractions(root, expirationTime);1802 }1803 }1804 if (remainingExpirationTime === Sync) {1805 // Count the number of times the root synchronously re-renders without1806 // finishing. If there are too many, it indicates an infinite update loop.1807 if (root === rootWithNestedUpdates) {1808 nestedUpdateCount++;1809 } else {1810 nestedUpdateCount = 0;1811 rootWithNestedUpdates = root;1812 }1813 } else {1814 nestedUpdateCount = 0;1815 }1816 onCommitRoot(finishedWork.stateNode, expirationTime);1817 // Always call this before exiting `commitRoot`, to ensure that any1818 // additional work on this root is scheduled.1819 ensureRootIsScheduled(root);1820 if (hasUncaughtError) {1821 hasUncaughtError = false;1822 const error = firstUncaughtError;1823 firstUncaughtError = null;1824 throw error;1825 }1826 if ((executionContext & LegacyUnbatchedContext) !== NoContext) {1827 // This is a legacy edge case. We just committed the initial mount of1828 // a ReactDOM.render-ed root inside of batchedUpdates. The commit fired1829 // synchronously, but layout updates should be deferred until the end1830 // of the batch.1831 return null;1832 }1833 // If layout work was scheduled, flush it now.1834 flushSyncCallbackQueue();1835 return null;1836}1837function commitBeforeMutationEffects() {1838 while (nextEffect !== null) {1839 const effectTag = nextEffect.effectTag;1840 if ((effectTag & Snapshot) !== NoEffect) {1841 setCurrentDebugFiberInDEV(nextEffect);1842 recordEffect();1843 const current = nextEffect.alternate;1844 commitBeforeMutationEffectOnFiber(current, nextEffect);1845 resetCurrentDebugFiberInDEV();1846 }1847 if ((effectTag & Passive) !== NoEffect) {1848 // If there are passive effects, schedule a callback to flush at1849 // the earliest opportunity.1850 if (!rootDoesHavePassiveEffects) {1851 rootDoesHavePassiveEffects = true;1852 scheduleCallback(NormalPriority, () => {1853 flushPassiveEffects();1854 return null;1855 });1856 }1857 }1858 nextEffect = nextEffect.nextEffect;1859 }1860}1861function commitMutationEffects(root: FiberRoot, renderPriorityLevel) {1862 // TODO: Should probably move the bulk of this function to commitWork.1863 while (nextEffect !== null) {1864 setCurrentDebugFiberInDEV(nextEffect);1865 const effectTag = nextEffect.effectTag;1866 if (effectTag & ContentReset) {1867 commitResetTextContent(nextEffect);1868 }1869 if (effectTag & Ref) {1870 const current = nextEffect.alternate;1871 if (current !== null) {1872 commitDetachRef(current);1873 }1874 }1875 // The following switch statement is only concerned about placement,1876 // updates, and deletions. To avoid needing to add a case for every possible1877 // bitmap value, we remove the secondary effects from the effect tag and1878 // switch on that value.1879 let primaryEffectTag =1880 effectTag & (Placement | Update | Deletion | Hydrating);1881 switch (primaryEffectTag) {1882 case Placement: {1883 commitPlacement(nextEffect);1884 // Clear the "placement" from effect tag so that we know that this is1885 // inserted, before any life-cycles like componentDidMount gets called.1886 // TODO: findDOMNode doesn't rely on this any more but isMounted does1887 // and isMounted is deprecated anyway so we should be able to kill this.1888 nextEffect.effectTag &= ~Placement;1889 break;1890 }1891 case PlacementAndUpdate: {1892 // Placement1893 commitPlacement(nextEffect);1894 // Clear the "placement" from effect tag so that we know that this is1895 // inserted, before any life-cycles like componentDidMount gets called.1896 nextEffect.effectTag &= ~Placement;1897 // Update1898 const current = nextEffect.alternate;1899 commitWork(current, nextEffect);1900 break;1901 }1902 case Hydrating: {1903 nextEffect.effectTag &= ~Hydrating;1904 break;1905 }1906 case HydratingAndUpdate: {1907 nextEffect.effectTag &= ~Hydrating;1908 // Update1909 const current = nextEffect.alternate;1910 commitWork(current, nextEffect);1911 break;1912 }1913 case Update: {1914 const current = nextEffect.alternate;1915 commitWork(current, nextEffect);1916 break;1917 }1918 case Deletion: {1919 commitDeletion(root, nextEffect, renderPriorityLevel);1920 break;1921 }1922 }1923 // TODO: Only record a mutation effect if primaryEffectTag is non-zero.1924 recordEffect();1925 resetCurrentDebugFiberInDEV();1926 nextEffect = nextEffect.nextEffect;1927 }1928}1929function commitLayoutEffects(1930 root: FiberRoot,1931 committedExpirationTime: ExpirationTime,1932) {1933 // TODO: Should probably move the bulk of this function to commitWork.1934 while (nextEffect !== null) {1935 setCurrentDebugFiberInDEV(nextEffect);1936 const effectTag = nextEffect.effectTag;1937 if (effectTag & (Update | Callback)) {1938 recordEffect();1939 const current = nextEffect.alternate;1940 commitLayoutEffectOnFiber(1941 root,1942 current,1943 nextEffect,1944 committedExpirationTime,1945 );1946 }1947 if (effectTag & Ref) {1948 recordEffect();1949 commitAttachRef(nextEffect);1950 }1951 resetCurrentDebugFiberInDEV();1952 nextEffect = nextEffect.nextEffect;1953 }1954}1955export function flushPassiveEffects() {1956 if (pendingPassiveEffectsRenderPriority !== NoPriority) {1957 const priorityLevel =1958 pendingPassiveEffectsRenderPriority > NormalPriority1959 ? NormalPriority1960 : pendingPassiveEffectsRenderPriority;1961 pendingPassiveEffectsRenderPriority = NoPriority;1962 return runWithPriority(priorityLevel, flushPassiveEffectsImpl);1963 }1964}1965export function enqueuePendingPassiveHookEffectMount(1966 fiber: Fiber,1967 effect: HookEffect,1968): void {1969 if (runAllPassiveEffectDestroysBeforeCreates) {1970 pendingPassiveHookEffectsMount.push(effect, fiber);1971 if (!rootDoesHavePassiveEffects) {1972 rootDoesHavePassiveEffects = true;1973 scheduleCallback(NormalPriority, () => {1974 flushPassiveEffects();1975 return null;1976 });1977 }1978 }1979}1980export function enqueuePendingPassiveHookEffectUnmount(1981 fiber: Fiber,1982 effect: HookEffect,1983): void {1984 if (runAllPassiveEffectDestroysBeforeCreates) {1985 pendingPassiveHookEffectsUnmount.push(effect, fiber);1986 if (!rootDoesHavePassiveEffects) {1987 rootDoesHavePassiveEffects = true;1988 scheduleCallback(NormalPriority, () => {1989 flushPassiveEffects();1990 return null;1991 });1992 }1993 }1994}1995function invokePassiveEffectCreate(effect: HookEffect): void {1996 const create = effect.create;1997 effect.destroy = create();1998}1999function flushPassiveEffectsImpl() {2000 if (rootWithPendingPassiveEffects === null) {2001 return false;2002 }2003 const root = rootWithPendingPassiveEffects;2004 const expirationTime = pendingPassiveEffectsExpirationTime;2005 rootWithPendingPassiveEffects = null;2006 pendingPassiveEffectsExpirationTime = NoWork;2007 invariant(2008 (executionContext & (RenderContext | CommitContext)) === NoContext,2009 'Cannot flush passive effects while already rendering.',2010 );2011 const prevExecutionContext = executionContext;2012 executionContext |= CommitContext;2013 const prevInteractions = pushInteractions(root);2014 if (runAllPassiveEffectDestroysBeforeCreates) {2015 // It's important that ALL pending passive effect destroy functions are called2016 // before ANY passive effect create functions are called.2017 // Otherwise effects in sibling components might interfere with each other.2018 // e.g. a destroy function in one component may unintentionally override a ref2019 // value set by a create function in another component.2020 // Layout effects have the same constraint.2021 // First pass: Destroy stale passive effects.2022 let unmountEffects = pendingPassiveHookEffectsUnmount;2023 pendingPassiveHookEffectsUnmount = [];2024 for (let i = 0; i < unmountEffects.length; i += 2) {2025 const effect = ((unmountEffects[i]: any): HookEffect);2026 const fiber = ((unmountEffects[i + 1]: any): Fiber);2027 const destroy = effect.destroy;2028 effect.destroy = undefined;2029 if (typeof destroy === 'function') {2030 if (true) {2031 setCurrentDebugFiberInDEV(fiber);2032 invokeGuardedCallback(null, destroy, null);2033 if (hasCaughtError()) {2034 invariant(fiber !== null, 'Should be working on an effect.');2035 const error = clearCaughtError();2036 captureCommitPhaseError(fiber, error);2037 }2038 resetCurrentDebugFiberInDEV();2039 } else {2040 try {2041 destroy();2042 } catch (error) {2043 invariant(fiber !== null, 'Should be working on an effect.');2044 captureCommitPhaseError(fiber, error);2045 }2046 }2047 }2048 }2049 // Second pass: Create new passive effects.2050 let mountEffects = pendingPassiveHookEffectsMount;2051 pendingPassiveHookEffectsMount = [];2052 for (let i = 0; i < mountEffects.length; i += 2) {2053 const effect = ((mountEffects[i]: any): HookEffect);2054 const fiber = ((mountEffects[i + 1]: any): Fiber);2055 if (true) {2056 setCurrentDebugFiberInDEV(fiber);2057 invokeGuardedCallback(null, invokePassiveEffectCreate, null, effect);2058 if (hasCaughtError()) {2059 invariant(fiber !== null, 'Should be working on an effect.');2060 const error = clearCaughtError();2061 captureCommitPhaseError(fiber, error);2062 }2063 resetCurrentDebugFiberInDEV();2064 } else {2065 try {2066 const create = effect.create;2067 effect.destroy = create();2068 } catch (error) {2069 invariant(fiber !== null, 'Should be working on an effect.');2070 captureCommitPhaseError(fiber, error);2071 }2072 }2073 }2074 } else {2075 // Note: This currently assumes there are no passive effects on the root fiber2076 // because the root is not part of its own effect list.2077 // This could change in the future.2078 let effect = root.current.firstEffect;2079 while (effect !== null) {2080 if (true) {2081 setCurrentDebugFiberInDEV(effect);2082 invokeGuardedCallback(null, commitPassiveHookEffects, null, effect);2083 if (hasCaughtError()) {2084 invariant(effect !== null, 'Should be working on an effect.');2085 const error = clearCaughtError();2086 captureCommitPhaseError(effect, error);2087 }2088 resetCurrentDebugFiberInDEV();2089 } else {2090 try {2091 commitPassiveHookEffects(effect);2092 } catch (error) {2093 invariant(effect !== null, 'Should be working on an effect.');2094 captureCommitPhaseError(effect, error);2095 }2096 }2097 const nextNextEffect = effect.nextEffect;2098 // Remove nextEffect pointer to assist GC2099 effect.nextEffect = null;2100 effect = nextNextEffect;2101 }2102 }2103 if (enableSchedulerTracing) {2104 popInteractions(((prevInteractions: any): Set<Interaction>));2105 finishPendingInteractions(root, expirationTime);2106 }2107 executionContext = prevExecutionContext;2108 flushSyncCallbackQueue();2109 // If additional passive effects were scheduled, increment a counter. If this2110 // exceeds the limit, we'll fire a warning.2111 nestedPassiveUpdateCount =2112 rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;2113 return true;2114}2115export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {2116 return (2117 legacyErrorBoundariesThatAlreadyFailed !== null &&2118 legacyErrorBoundariesThatAlreadyFailed.has(instance)2119 );2120}2121export function markLegacyErrorBoundaryAsFailed(instance: mixed) {2122 if (legacyErrorBoundariesThatAlreadyFailed === null) {2123 legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);2124 } else {2125 legacyErrorBoundariesThatAlreadyFailed.add(instance);2126 }2127}2128function prepareToThrowUncaughtError(error: mixed) {2129 if (!hasUncaughtError) {2130 hasUncaughtError = true;2131 firstUncaughtError = error;2132 }2133}2134export const onUncaughtError = prepareToThrowUncaughtError;2135function captureCommitPhaseErrorOnRoot(2136 rootFiber: Fiber,2137 sourceFiber: Fiber,2138 error: mixed,2139) {2140 const errorInfo = createCapturedValue(error, sourceFiber);2141 const update = createRootErrorUpdate(rootFiber, errorInfo, Sync);2142 enqueueUpdate(rootFiber, update);2143 const root = markUpdateTimeFromFiberToRoot(rootFiber, Sync);2144 if (root !== null) {2145 ensureRootIsScheduled(root);2146 schedulePendingInteractions(root, Sync);2147 }2148}2149export function captureCommitPhaseError(sourceFiber: Fiber, error: mixed) {2150 if (sourceFiber.tag === HostRoot) {2151 // Error was thrown at the root. There is no parent, so the root2152 // itself should capture it.2153 captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);2154 return;2155 }2156 let fiber = sourceFiber.return;2157 while (fiber !== null) {2158 if (fiber.tag === HostRoot) {2159 captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);2160 return;2161 } else if (fiber.tag === ClassComponent) {2162 const ctor = fiber.type;2163 const instance = fiber.stateNode;2164 if (2165 typeof ctor.getDerivedStateFromError === 'function' ||2166 (typeof instance.componentDidCatch === 'function' &&2167 !isAlreadyFailedLegacyErrorBoundary(instance))2168 ) {2169 const errorInfo = createCapturedValue(error, sourceFiber);2170 const update = createClassErrorUpdate(2171 fiber,2172 errorInfo,2173 // TODO: This is always sync2174 Sync,2175 );2176 enqueueUpdate(fiber, update);2177 const root = markUpdateTimeFromFiberToRoot(fiber, Sync);2178 if (root !== null) {2179 ensureRootIsScheduled(root);2180 schedulePendingInteractions(root, Sync);2181 }2182 return;2183 }2184 }2185 fiber = fiber.return;2186 }2187}2188export function pingSuspendedRoot(2189 root: FiberRoot,2190 thenable: Thenable,2191 suspendedTime: ExpirationTime,2192) {2193 const pingCache = root.pingCache;2194 if (pingCache !== null) {2195 // The thenable resolved, so we no longer need to memoize, because it will2196 // never be thrown again.2197 pingCache.delete(thenable);2198 }2199 if (workInProgressRoot === root && renderExpirationTime === suspendedTime) {2200 // Received a ping at the same priority level at which we're currently2201 // rendering. We might want to restart this render. This should mirror2202 // the logic of whether or not a root suspends once it completes.2203 // TODO: If we're rendering sync either due to Sync, Batched or expired,2204 // we should probably never restart.2205 // If we're suspended with delay, we'll always suspend so we can always2206 // restart. If we're suspended without any updates, it might be a retry.2207 // If it's early in the retry we can restart. We can't know for sure2208 // whether we'll eventually process an update during this render pass,2209 // but it's somewhat unlikely that we get to a ping before that, since2210 // getting to the root most update is usually very fast.2211 if (2212 workInProgressRootExitStatus === RootSuspendedWithDelay ||2213 (workInProgressRootExitStatus === RootSuspended &&2214 workInProgressRootLatestProcessedExpirationTime === Sync &&2215 now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS)2216 ) {2217 // Restart from the root. Don't need to schedule a ping because2218 // we're already working on this tree.2219 prepareFreshStack(root, renderExpirationTime);2220 } else {2221 // Even though we can't restart right now, we might get an2222 // opportunity later. So we mark this render as having a ping.2223 workInProgressRootHasPendingPing = true;2224 }2225 return;2226 }2227 if (!isRootSuspendedAtTime(root, suspendedTime)) {2228 // The root is no longer suspended at this time.2229 return;2230 }2231 const lastPingedTime = root.lastPingedTime;2232 if (lastPingedTime !== NoWork && lastPingedTime < suspendedTime) {2233 // There's already a lower priority ping scheduled.2234 return;2235 }2236 // Mark the time at which this ping was scheduled.2237 root.lastPingedTime = suspendedTime;2238 if (!enableTrainModelFix && root.finishedExpirationTime === suspendedTime) {2239 // If there's a pending fallback waiting to commit, throw it away.2240 root.finishedExpirationTime = NoWork;2241 root.finishedWork = null;2242 }2243 ensureRootIsScheduled(root);2244 schedulePendingInteractions(root, suspendedTime);2245}2246function retryTimedOutBoundary(2247 boundaryFiber: Fiber,2248 retryTime: ExpirationTime,2249) {2250 // The boundary fiber (a Suspense component or SuspenseList component)2251 // previously was rendered in its fallback state. One of the promises that2252 // suspended it has resolved, which means at least part of the tree was2253 // likely unblocked. Try rendering again, at a new expiration time.2254 if (retryTime === NoWork) {2255 const suspenseConfig = null; // Retries don't carry over the already committed update.2256 const currentTime = requestCurrentTimeForUpdate();2257 retryTime = computeExpirationForFiber(2258 currentTime,2259 boundaryFiber,2260 suspenseConfig,2261 );2262 }2263 // TODO: Special case idle priority?2264 const root = markUpdateTimeFromFiberToRoot(boundaryFiber, retryTime);2265 if (root !== null) {2266 ensureRootIsScheduled(root);2267 schedulePendingInteractions(root, retryTime);2268 }2269}2270export function retryDehydratedSuspenseBoundary(boundaryFiber: Fiber) {2271 const suspenseState: null | SuspenseState = boundaryFiber.memoizedState;2272 let retryTime = NoWork;2273 if (suspenseState !== null) {2274 retryTime = suspenseState.retryTime;2275 }2276 retryTimedOutBoundary(boundaryFiber, retryTime);2277}2278export function resolveRetryThenable(boundaryFiber: Fiber, thenable: Thenable) {2279 let retryTime = NoWork; // Default2280 let retryCache: WeakSet<Thenable> | Set<Thenable> | null;2281 if (enableSuspenseServerRenderer) {2282 switch (boundaryFiber.tag) {2283 case SuspenseComponent:2284 retryCache = boundaryFiber.stateNode;2285 const suspenseState: null | SuspenseState = boundaryFiber.memoizedState;2286 if (suspenseState !== null) {2287 retryTime = suspenseState.retryTime;2288 }2289 break;2290 case SuspenseListComponent:2291 retryCache = boundaryFiber.stateNode;2292 break;2293 default:2294 invariant(2295 false,2296 'Pinged unknown suspense boundary type. ' +2297 'This is probably a bug in React.',2298 );2299 }2300 } else {2301 retryCache = boundaryFiber.stateNode;2302 }2303 if (retryCache !== null) {2304 // The thenable resolved, so we no longer need to memoize, because it will2305 // never be thrown again.2306 retryCache.delete(thenable);2307 }2308 retryTimedOutBoundary(boundaryFiber, retryTime);2309}2310// Computes the next Just Noticeable Difference (JND) boundary.2311// The theory is that a person can't tell the difference between small differences in time.2312// Therefore, if we wait a bit longer than necessary that won't translate to a noticeable2313// difference in the experience. However, waiting for longer might mean that we can avoid2314// showing an intermediate loading state. The longer we have already waited, the harder it2315// is to tell small differences in time. Therefore, the longer we've already waited,2316// the longer we can wait additionally. At some point we have to give up though.2317// We pick a train model where the next boundary commits at a consistent schedule.2318// These particular numbers are vague estimates. We expect to adjust them based on research.2319function jnd(timeElapsed: number) {2320 return timeElapsed < 1202321 ? 1202322 : timeElapsed < 4802323 ? 4802324 : timeElapsed < 10802325 ? 10802326 : timeElapsed < 19202327 ? 19202328 : timeElapsed < 30002329 ? 30002330 : timeElapsed < 43202331 ? 43202332 : ceil(timeElapsed / 1960) * 1960;2333}2334function computeMsUntilSuspenseLoadingDelay(2335 mostRecentEventTime: ExpirationTime,2336 committedExpirationTime: ExpirationTime,2337 suspenseConfig: SuspenseConfig,2338) {2339 const busyMinDurationMs = (suspenseConfig.busyMinDurationMs: any) | 0;2340 if (busyMinDurationMs <= 0) {2341 return 0;2342 }2343 const busyDelayMs = (suspenseConfig.busyDelayMs: any) | 0;2344 // Compute the time until this render pass would expire.2345 const currentTimeMs: number = now();2346 const eventTimeMs: number = inferTimeFromExpirationTimeWithSuspenseConfig(2347 mostRecentEventTime,2348 suspenseConfig,2349 );2350 const timeElapsed = currentTimeMs - eventTimeMs;2351 if (timeElapsed <= busyDelayMs) {2352 // If we haven't yet waited longer than the initial delay, we don't2353 // have to wait any additional time.2354 return 0;2355 }2356 const msUntilTimeout = busyDelayMs + busyMinDurationMs - timeElapsed;2357 // This is the value that is passed to `setTimeout`.2358 return msUntilTimeout;2359}2360function checkForNestedUpdates() {2361 if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {2362 nestedUpdateCount = 0;2363 rootWithNestedUpdates = null;2364 invariant(2365 false,2366 'Maximum update depth exceeded. This can happen when a component ' +2367 'repeatedly calls setState inside componentWillUpdate or ' +2368 'componentDidUpdate. React limits the number of nested updates to ' +2369 'prevent infinite loops.',2370 );2371 }2372}2373function flushRenderPhaseStrictModeWarningsInDEV() {2374 if (true) {2375 ReactStrictModeWarnings.flushLegacyContextWarning();2376 if (warnAboutDeprecatedLifecycles) {2377 ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();2378 }2379 }2380}2381function stopFinishedWorkLoopTimer() {2382 const didCompleteRoot = true;2383 stopWorkLoopTimer(interruptedBy, didCompleteRoot);2384 interruptedBy = null;2385}2386function stopInterruptedWorkLoopTimer() {2387 // TODO: Track which fiber caused the interruption.2388 const didCompleteRoot = false;2389 stopWorkLoopTimer(interruptedBy, didCompleteRoot);2390 interruptedBy = null;2391}2392function checkForInterruption(2393 fiberThatReceivedUpdate: Fiber,2394 updateExpirationTime: ExpirationTime,2395) {2396 if (2397 enableUserTimingAPI &&2398 workInProgressRoot !== null &&2399 updateExpirationTime > renderExpirationTime2400 ) {2401 interruptedBy = fiberThatReceivedUpdate;2402 }2403}2404let didWarnStateUpdateForUnmountedComponent: Set<string> | null = null;2405function warnAboutUpdateOnUnmountedFiberInDEV(fiber) {2406 if (true) {2407 const tag = fiber.tag;2408 if (2409 tag !== HostRoot &&2410 tag !== ClassComponent &&2411 tag !== FunctionComponent &&2412 tag !== ForwardRef &&2413 tag !== MemoComponent &&2414 tag !== SimpleMemoComponent &&2415 tag !== Block2416 ) {2417 // Only warn for user-defined components, not internal ones like Suspense.2418 return;2419 }2420 if (2421 deferPassiveEffectCleanupDuringUnmount &&2422 runAllPassiveEffectDestroysBeforeCreates2423 ) {2424 // If there are pending passive effects unmounts for this Fiber,2425 // we can assume that they would have prevented this update.2426 if (pendingPassiveHookEffectsUnmount.indexOf(fiber) >= 0) {2427 return;2428 }2429 }2430 // We show the whole stack but dedupe on the top component's name because2431 // the problematic code almost always lies inside that component.2432 const componentName = getComponentName(fiber.type) || 'ReactComponent';2433 if (didWarnStateUpdateForUnmountedComponent !== null) {2434 if (didWarnStateUpdateForUnmountedComponent.has(componentName)) {2435 return;2436 }2437 didWarnStateUpdateForUnmountedComponent.add(componentName);2438 } else {2439 didWarnStateUpdateForUnmountedComponent = new Set([componentName]);2440 }2441 console.error(2442 "Can't perform a React state update on an unmounted component. This " +2443 'is a no-op, but it indicates a memory leak in your application. To ' +2444 'fix, cancel all subscriptions and asynchronous tasks in %s.%s',2445 tag === ClassComponent2446 ? 'the componentWillUnmount method'2447 : 'a useEffect cleanup function',2448 getStackByFiberInDevAndProd(fiber),2449 );2450 }2451}2452let beginWork;2453if (true && replayFailedUnitOfWorkWithInvokeGuardedCallback) {2454 let dummyFiber = null;2455 beginWork = (current, unitOfWork, expirationTime) => {2456 // If a component throws an error, we replay it again in a synchronously2457 // dispatched event, so that the debugger will treat it as an uncaught2458 // error See ReactErrorUtils for more information.2459 // Before entering the begin phase, copy the work-in-progress onto a dummy2460 // fiber. If beginWork throws, we'll use this to reset the state.2461 const originalWorkInProgressCopy = assignFiberPropertiesInDEV(2462 dummyFiber,2463 unitOfWork,2464 );2465 try {2466 let tmp = originalBeginWork(current, unitOfWork, expirationTime);2467 debugger;2468 return tmp;2469 } catch (originalError) {2470 if (2471 originalError !== null &&2472 typeof originalError === 'object' &&2473 typeof originalError.then === 'function'2474 ) {2475 // Don't replay promises. Treat everything else like an error.2476 throw originalError;2477 }2478 // Keep this code in sync with handleError; any changes here must have2479 // corresponding changes there.2480 resetContextDependencies();2481 resetHooksAfterThrow();2482 // Don't reset current debug fiber, since we're about to work on the2483 // same fiber again.2484 // Unwind the failed stack frame2485 unwindInterruptedWork(unitOfWork);2486 // Restore the original properties of the fiber.2487 assignFiberPropertiesInDEV(unitOfWork, originalWorkInProgressCopy);2488 if (enableProfilerTimer && unitOfWork.mode & ProfileMode) {2489 // Reset the profiler timer.2490 startProfilerTimer(unitOfWork);2491 }2492 // Run beginWork again.2493 invokeGuardedCallback(2494 null,2495 originalBeginWork,...
ReactFiberWorkLoop.new.js
Source:ReactFiberWorkLoop.new.js
...1114 let erroredWork = workInProgress;1115 try {1116 // Reset module-level state that was set during the render phase.1117 resetContextDependencies();1118 resetHooksAfterThrow();1119 resetCurrentDebugFiberInDEV();1120 // TODO: I found and added this missing line while investigating a1121 // separate issue. Write a regression test using string refs.1122 ReactCurrentOwner.current = null;1123 if (erroredWork === null || erroredWork.return === null) {1124 // Expected to be working on a non-root fiber. This is a fatal error1125 // because there's no ancestor that can handle it; the root is1126 // supposed to capture all errors that weren't caught by an error1127 // boundary.1128 workInProgressRootExitStatus = RootFatalErrored;1129 workInProgressRootFatalError = thrownValue;1130 // Set `workInProgress` to null. This represents advancing to the next1131 // sibling, or the parent if there are no siblings. But since the root1132 // has no siblings nor a parent, we set it to null. Usually this is1133 // handled by `completeUnitOfWork` or `unwindWork`, but since we're1134 // intentionally not calling those, we need set it here.1135 // TODO: Consider calling `unwindWork` to pop the contexts.1136 workInProgress = null;1137 return;1138 }1139 if (enableProfilerTimer && erroredWork.mode & ProfileMode) {1140 // Record the time spent rendering before an error was thrown. This1141 // avoids inaccurate Profiler durations in the case of a1142 // suspended render.1143 stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true);1144 }1145 throwException(1146 root,1147 erroredWork.return,1148 erroredWork,1149 thrownValue,1150 workInProgressRootRenderLanes,1151 );1152 completeUnitOfWork(erroredWork);1153 } catch (yetAnotherThrownValue) {1154 // Something in the return path also threw.1155 thrownValue = yetAnotherThrownValue;1156 if (workInProgress === erroredWork && erroredWork !== null) {1157 // If this boundary has already errored, then we had trouble processing1158 // the error. Bubble it to the next boundary.1159 erroredWork = erroredWork.return;1160 workInProgress = erroredWork;1161 } else {1162 erroredWork = workInProgress;1163 }1164 continue;1165 }1166 // Return to the normal work loop.1167 return;1168 } while (true);1169}1170function pushDispatcher() {1171 const prevDispatcher = ReactCurrentDispatcher.current;1172 ReactCurrentDispatcher.current = ContextOnlyDispatcher;1173 if (prevDispatcher === null) {1174 // The React isomorphic package does not include a default dispatcher.1175 // Instead the first renderer will lazily attach one, in order to give1176 // nicer error messages.1177 return ContextOnlyDispatcher;1178 } else {1179 return prevDispatcher;1180 }1181}1182function popDispatcher(prevDispatcher) {1183 ReactCurrentDispatcher.current = prevDispatcher;1184}1185export function markCommitTimeOfFallback() {1186 globalMostRecentFallbackTime = now();1187}1188export function markSkippedUpdateLanes(lane: Lane | Lanes): void {1189 workInProgressRootSkippedLanes = mergeLanes(1190 lane,1191 workInProgressRootSkippedLanes,1192 );1193}1194export function renderDidSuspend(): void {1195 if (workInProgressRootExitStatus === RootIncomplete) {1196 workInProgressRootExitStatus = RootSuspended;1197 }1198}1199export function renderDidSuspendDelayIfPossible(): void {1200 if (1201 workInProgressRootExitStatus === RootIncomplete ||1202 workInProgressRootExitStatus === RootSuspended1203 ) {1204 workInProgressRootExitStatus = RootSuspendedWithDelay;1205 }1206 // Check if there are updates that we skipped tree that might have unblocked1207 // this render.1208 if (1209 workInProgressRoot !== null &&1210 (includesNonIdleWork(workInProgressRootSkippedLanes) ||1211 includesNonIdleWork(workInProgressRootUpdatedLanes))1212 ) {1213 // Mark the current render as suspended so that we switch to working on1214 // the updates that were skipped. Usually we only suspend at the end of1215 // the render phase.1216 // TODO: We should probably always mark the root as suspended immediately1217 // (inside this function), since by suspending at the end of the render1218 // phase introduces a potential mistake where we suspend lanes that were1219 // pinged or updated while we were rendering.1220 markRootSuspended(workInProgressRoot, workInProgressRootRenderLanes);1221 }1222}1223export function renderDidError() {1224 if (workInProgressRootExitStatus !== RootCompleted) {1225 workInProgressRootExitStatus = RootErrored;1226 }1227}1228// Called during render to determine if anything has suspended.1229// Returns false if we're not sure.1230export function renderHasNotSuspendedYet(): boolean {1231 // If something errored or completed, we can't really be sure,1232 // so those are false.1233 return workInProgressRootExitStatus === RootIncomplete;1234}1235function renderRootSync(root: FiberRoot, lanes: Lanes) {1236 const prevExecutionContext = executionContext;1237 executionContext |= RenderContext;1238 const prevDispatcher = pushDispatcher();1239 // If the root or lanes have changed, throw out the existing stack1240 // and prepare a fresh one. Otherwise we'll continue where we left off.1241 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1242 if (enableUpdaterTracking) {1243 if (isDevToolsPresent) {1244 const memoizedUpdaters = root.memoizedUpdaters;1245 if (memoizedUpdaters.size > 0) {1246 restorePendingUpdaters(root, workInProgressRootRenderLanes);1247 memoizedUpdaters.clear();1248 }1249 // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.1250 // If we bailout on this work, we'll move them back (like above).1251 // It's important to move them now in case the work spawns more work at the same priority with different updaters.1252 // That way we can keep the current update and future updates separate.1253 movePendingFibersToMemoized(root, lanes);1254 }1255 }1256 prepareFreshStack(root, lanes);1257 }1258 if (__DEV__) {1259 if (enableDebugTracing) {1260 logRenderStarted(lanes);1261 }1262 }1263 if (enableSchedulingProfiler) {1264 markRenderStarted(lanes);1265 }1266 do {1267 try {1268 workLoopSync();1269 break;1270 } catch (thrownValue) {1271 handleError(root, thrownValue);1272 }1273 } while (true);1274 resetContextDependencies();1275 executionContext = prevExecutionContext;1276 popDispatcher(prevDispatcher);1277 if (workInProgress !== null) {1278 // This is a sync render, so we should have finished the whole tree.1279 invariant(1280 false,1281 'Cannot commit an incomplete root. This error is likely caused by a ' +1282 'bug in React. Please file an issue.',1283 );1284 }1285 if (__DEV__) {1286 if (enableDebugTracing) {1287 logRenderStopped();1288 }1289 }1290 if (enableSchedulingProfiler) {1291 markRenderStopped();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 // Already timed out, so perform work without checking if we need to yield.1302 while (workInProgress !== null) {1303 performUnitOfWork(workInProgress);1304 }1305}1306function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {1307 const prevExecutionContext = executionContext;1308 executionContext |= RenderContext;1309 const prevDispatcher = pushDispatcher();1310 // If the root or lanes have changed, throw out the existing stack1311 // and prepare a fresh one. Otherwise we'll continue where we left off.1312 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1313 if (enableUpdaterTracking) {1314 if (isDevToolsPresent) {1315 const memoizedUpdaters = root.memoizedUpdaters;1316 if (memoizedUpdaters.size > 0) {1317 restorePendingUpdaters(root, workInProgressRootRenderLanes);1318 memoizedUpdaters.clear();1319 }1320 // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.1321 // If we bailout on this work, we'll move them back (like above).1322 // It's important to move them now in case the work spawns more work at the same priority with different updaters.1323 // That way we can keep the current update and future updates separate.1324 movePendingFibersToMemoized(root, lanes);1325 }1326 }1327 resetRenderTimer();1328 prepareFreshStack(root, lanes);1329 }1330 if (__DEV__) {1331 if (enableDebugTracing) {1332 logRenderStarted(lanes);1333 }1334 }1335 if (enableSchedulingProfiler) {1336 markRenderStarted(lanes);1337 }1338 do {1339 try {1340 workLoopConcurrent();1341 break;1342 } catch (thrownValue) {1343 handleError(root, thrownValue);1344 }1345 } while (true);1346 resetContextDependencies();1347 popDispatcher(prevDispatcher);1348 executionContext = prevExecutionContext;1349 if (__DEV__) {1350 if (enableDebugTracing) {1351 logRenderStopped();1352 }1353 }1354 // Check if the tree has completed.1355 if (workInProgress !== null) {1356 // Still work remaining.1357 if (enableSchedulingProfiler) {1358 markRenderYielded();1359 }1360 return RootIncomplete;1361 } else {1362 // Completed the tree.1363 if (enableSchedulingProfiler) {1364 markRenderStopped();1365 }1366 // Set this to null to indicate there's no in-progress render.1367 workInProgressRoot = null;1368 workInProgressRootRenderLanes = NoLanes;1369 // Return the final exit status.1370 return workInProgressRootExitStatus;1371 }1372}1373/** @noinline */1374function workLoopConcurrent() {1375 // Perform work until Scheduler asks us to yield1376 while (workInProgress !== null && !shouldYield()) {1377 performUnitOfWork(workInProgress);1378 }1379}1380function performUnitOfWork(unitOfWork: Fiber): void {1381 // 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 setCurrentDebugFiberInDEV(unitOfWork);1386 let next;1387 if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {1388 startProfilerTimer(unitOfWork);1389 next = beginWork(current, unitOfWork, subtreeRenderLanes);1390 stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);1391 } else {1392 next = beginWork(current, unitOfWork, subtreeRenderLanes);1393 }1394 resetCurrentDebugFiberInDEV();1395 unitOfWork.memoizedProps = unitOfWork.pendingProps;1396 if (next === null) {1397 // If this doesn't spawn new work, complete the current work.1398 completeUnitOfWork(unitOfWork);1399 } else {1400 workInProgress = next;1401 }1402 ReactCurrentOwner.current = null;1403}1404function completeUnitOfWork(unitOfWork: Fiber): void {1405 // Attempt to complete the current unit of work, then move to the next1406 // sibling. If there are no more siblings, return to the parent fiber.1407 let completedWork = unitOfWork;1408 do {1409 // The current, flushed, state of this fiber is the alternate. Ideally1410 // nothing should rely on this, but relying on it here means that we don't1411 // need an additional field on the work in progress.1412 const current = completedWork.alternate;1413 const returnFiber = completedWork.return;1414 // Check if the work completed or if something threw.1415 if ((completedWork.flags & Incomplete) === NoFlags) {1416 setCurrentDebugFiberInDEV(completedWork);1417 let next;1418 if (1419 !enableProfilerTimer ||1420 (completedWork.mode & ProfileMode) === NoMode1421 ) {1422 next = completeWork(current, completedWork, subtreeRenderLanes);1423 } else {1424 startProfilerTimer(completedWork);1425 next = completeWork(current, completedWork, subtreeRenderLanes);1426 // Update render duration assuming we didn't error.1427 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1428 }1429 resetCurrentDebugFiberInDEV();1430 if (next !== null) {1431 // Completing this fiber spawned new work. Work on that next.1432 workInProgress = next;1433 return;1434 }1435 } else {1436 // This fiber did not complete because something threw. Pop values off1437 // the stack without entering the complete phase. If this is a boundary,1438 // capture values if possible.1439 const next = unwindWork(completedWork, subtreeRenderLanes);1440 // Because this fiber did not complete, don't reset its lanes.1441 if (next !== null) {1442 // If completing this work spawned new work, do that next. We'll come1443 // back here again.1444 // Since we're restarting, remove anything that is not a host effect1445 // from the effect tag.1446 next.flags &= HostEffectMask;1447 workInProgress = next;1448 return;1449 }1450 if (1451 enableProfilerTimer &&1452 (completedWork.mode & ProfileMode) !== NoMode1453 ) {1454 // Record the render duration for the fiber that errored.1455 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1456 // Include the time spent working on failed children before continuing.1457 let actualDuration = completedWork.actualDuration;1458 let child = completedWork.child;1459 while (child !== null) {1460 actualDuration += child.actualDuration;1461 child = child.sibling;1462 }1463 completedWork.actualDuration = actualDuration;1464 }1465 if (returnFiber !== null) {1466 // Mark the parent fiber as incomplete and clear its subtree flags.1467 returnFiber.flags |= Incomplete;1468 returnFiber.subtreeFlags = NoFlags;1469 returnFiber.deletions = null;1470 }1471 }1472 const siblingFiber = completedWork.sibling;1473 if (siblingFiber !== null) {1474 // If there is more work to do in this returnFiber, do that next.1475 workInProgress = siblingFiber;1476 return;1477 }1478 // Otherwise, return to the parent1479 completedWork = returnFiber;1480 // Update the next thing we're working on in case something throws.1481 workInProgress = completedWork;1482 } while (completedWork !== null);1483 // We've reached the root.1484 if (workInProgressRootExitStatus === RootIncomplete) {1485 workInProgressRootExitStatus = RootCompleted;1486 }1487}1488function commitRoot(root) {1489 // TODO: This no longer makes any sense. We already wrap the mutation and1490 // layout phases. Should be able to remove.1491 const previousUpdateLanePriority = getCurrentUpdatePriority();1492 const prevTransition = ReactCurrentBatchConfig.transition;1493 try {1494 ReactCurrentBatchConfig.transition = 0;1495 setCurrentUpdatePriority(DiscreteEventPriority);1496 commitRootImpl(root, previousUpdateLanePriority);1497 } finally {1498 ReactCurrentBatchConfig.transition = prevTransition;1499 setCurrentUpdatePriority(previousUpdateLanePriority);1500 }1501 return null;1502}1503function commitRootImpl(root, renderPriorityLevel) {1504 do {1505 // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which1506 // means `flushPassiveEffects` will sometimes result in additional1507 // passive effects. So we need to keep flushing in a loop until there are1508 // no more pending effects.1509 // TODO: Might be better if `flushPassiveEffects` did not automatically1510 // flush synchronous work at the end, to avoid factoring hazards like this.1511 flushPassiveEffects();1512 } while (rootWithPendingPassiveEffects !== null);1513 flushRenderPhaseStrictModeWarningsInDEV();1514 invariant(1515 (executionContext & (RenderContext | CommitContext)) === NoContext,1516 'Should not already be working.',1517 );1518 const finishedWork = root.finishedWork;1519 const lanes = root.finishedLanes;1520 if (__DEV__) {1521 if (enableDebugTracing) {1522 logCommitStarted(lanes);1523 }1524 }1525 if (enableSchedulingProfiler) {1526 markCommitStarted(lanes);1527 }1528 if (finishedWork === null) {1529 if (__DEV__) {1530 if (enableDebugTracing) {1531 logCommitStopped();1532 }1533 }1534 if (enableSchedulingProfiler) {1535 markCommitStopped();1536 }1537 return null;1538 } else {1539 if (__DEV__) {1540 if (lanes === NoLanes) {1541 console.error(1542 'root.finishedLanes should not be empty during a commit. This is a ' +1543 'bug in React.',1544 );1545 }1546 }1547 }1548 root.finishedWork = null;1549 root.finishedLanes = NoLanes;1550 invariant(1551 finishedWork !== root.current,1552 'Cannot commit the same tree as before. This error is likely caused by ' +1553 'a bug in React. Please file an issue.',1554 );1555 // commitRoot never returns a continuation; it always finishes synchronously.1556 // So we can clear these now to allow a new callback to be scheduled.1557 root.callbackNode = null;1558 root.callbackPriority = NoLane;1559 // Update the first and last pending times on this root. The new first1560 // pending time is whatever is left on the root fiber.1561 let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);1562 markRootFinished(root, remainingLanes);1563 if (root === workInProgressRoot) {1564 // We can reset these now that they are finished.1565 workInProgressRoot = null;1566 workInProgress = null;1567 workInProgressRootRenderLanes = NoLanes;1568 } else {1569 // This indicates that the last root we worked on is not the same one that1570 // we're committing now. This most commonly happens when a suspended root1571 // times out.1572 }1573 // If there are pending passive effects, schedule a callback to process them.1574 // Do this as early as possible, so it is queued before anything else that1575 // might get scheduled in the commit phase. (See #16714.)1576 // TODO: Delete all other places that schedule the passive effect callback1577 // They're redundant.1578 if (1579 (finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||1580 (finishedWork.flags & PassiveMask) !== NoFlags1581 ) {1582 if (!rootDoesHavePassiveEffects) {1583 rootDoesHavePassiveEffects = true;1584 scheduleCallback(NormalSchedulerPriority, () => {1585 flushPassiveEffects();1586 return null;1587 });1588 }1589 }1590 // Check if there are any effects in the whole tree.1591 // TODO: This is left over from the effect list implementation, where we had1592 // to check for the existence of `firstEffect` to satisfy Flow. I think the1593 // only other reason this optimization exists is because it affects profiling.1594 // Reconsider whether this is necessary.1595 const subtreeHasEffects =1596 (finishedWork.subtreeFlags &1597 (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1598 NoFlags;1599 const rootHasEffect =1600 (finishedWork.flags &1601 (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1602 NoFlags;1603 if (subtreeHasEffects || rootHasEffect) {1604 const prevTransition = ReactCurrentBatchConfig.transition;1605 ReactCurrentBatchConfig.transition = 0;1606 const previousPriority = getCurrentUpdatePriority();1607 setCurrentUpdatePriority(DiscreteEventPriority);1608 const prevExecutionContext = executionContext;1609 executionContext |= CommitContext;1610 // Reset this to null before calling lifecycles1611 ReactCurrentOwner.current = null;1612 // The commit phase is broken into several sub-phases. We do a separate pass1613 // of the effect list for each phase: all mutation effects come before all1614 // layout effects, and so on.1615 // The first phase a "before mutation" phase. We use this phase to read the1616 // state of the host tree right before we mutate it. This is where1617 // getSnapshotBeforeUpdate is called.1618 const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(1619 root,1620 finishedWork,1621 );1622 if (enableProfilerTimer) {1623 // Mark the current commit time to be shared by all Profilers in this1624 // batch. This enables them to be grouped later.1625 recordCommitTime();1626 }1627 if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {1628 // Track the root here, rather than in commitLayoutEffects(), because of ref setters.1629 // Updates scheduled during ref detachment should also be flagged.1630 rootCommittingMutationOrLayoutEffects = root;1631 }1632 // The next phase is the mutation phase, where we mutate the host tree.1633 commitMutationEffects(root, finishedWork, lanes);1634 if (enableCreateEventHandleAPI) {1635 if (shouldFireAfterActiveInstanceBlur) {1636 afterActiveInstanceBlur();1637 }1638 }1639 resetAfterCommit(root.containerInfo);1640 // The work-in-progress tree is now the current tree. This must come after1641 // the mutation phase, so that the previous tree is still current during1642 // componentWillUnmount, but before the layout phase, so that the finished1643 // work is current during componentDidMount/Update.1644 root.current = finishedWork;1645 // The next phase is the layout phase, where we call effects that read1646 // the host tree after it's been mutated. The idiomatic use case for this is1647 // layout, but class component lifecycles also fire here for legacy reasons.1648 if (__DEV__) {1649 if (enableDebugTracing) {1650 logLayoutEffectsStarted(lanes);1651 }1652 }1653 if (enableSchedulingProfiler) {1654 markLayoutEffectsStarted(lanes);1655 }1656 commitLayoutEffects(finishedWork, root, lanes);1657 if (__DEV__) {1658 if (enableDebugTracing) {1659 logLayoutEffectsStopped();1660 }1661 }1662 if (enableSchedulingProfiler) {1663 markLayoutEffectsStopped();1664 }1665 if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {1666 rootCommittingMutationOrLayoutEffects = null;1667 }1668 // Tell Scheduler to yield at the end of the frame, so the browser has an1669 // opportunity to paint.1670 requestPaint();1671 executionContext = prevExecutionContext;1672 // Reset the priority to the previous non-sync value.1673 setCurrentUpdatePriority(previousPriority);1674 ReactCurrentBatchConfig.transition = prevTransition;1675 } else {1676 // No effects.1677 root.current = finishedWork;1678 // Measure these anyway so the flamegraph explicitly shows that there were1679 // no effects.1680 // TODO: Maybe there's a better way to report this.1681 if (enableProfilerTimer) {1682 recordCommitTime();1683 }1684 }1685 const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;1686 if (rootDoesHavePassiveEffects) {1687 // This commit has passive effects. Stash a reference to them. But don't1688 // schedule a callback until after flushing layout work.1689 rootDoesHavePassiveEffects = false;1690 rootWithPendingPassiveEffects = root;1691 pendingPassiveEffectsLanes = lanes;1692 }1693 // Read this again, since an effect might have updated it1694 remainingLanes = root.pendingLanes;1695 // Check if there's remaining work on this root1696 if (remainingLanes === NoLanes) {1697 // If there's no remaining work, we can clear the set of already failed1698 // error boundaries.1699 legacyErrorBoundariesThatAlreadyFailed = null;1700 }1701 if (__DEV__ && enableStrictEffects) {1702 if (!rootDidHavePassiveEffects) {1703 commitDoubleInvokeEffectsInDEV(root.current, false);1704 }1705 }1706 if (includesSomeLane(remainingLanes, (SyncLane: Lane))) {1707 if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {1708 markNestedUpdateScheduled();1709 }1710 // Count the number of times the root synchronously re-renders without1711 // finishing. If there are too many, it indicates an infinite update loop.1712 if (root === rootWithNestedUpdates) {1713 nestedUpdateCount++;1714 } else {1715 nestedUpdateCount = 0;1716 rootWithNestedUpdates = root;1717 }1718 } else {1719 nestedUpdateCount = 0;1720 }1721 onCommitRootDevTools(finishedWork.stateNode, renderPriorityLevel);1722 if (enableUpdaterTracking) {1723 if (isDevToolsPresent) {1724 root.memoizedUpdaters.clear();1725 }1726 }1727 if (__DEV__) {1728 onCommitRootTestSelector();1729 }1730 // Always call this before exiting `commitRoot`, to ensure that any1731 // additional work on this root is scheduled.1732 ensureRootIsScheduled(root, now());1733 if (hasUncaughtError) {1734 hasUncaughtError = false;1735 const error = firstUncaughtError;1736 firstUncaughtError = null;1737 throw error;1738 }1739 // If the passive effects are the result of a discrete render, flush them1740 // synchronously at the end of the current task so that the result is1741 // immediately observable. Otherwise, we assume that they are not1742 // order-dependent and do not need to be observed by external systems, so we1743 // can wait until after paint.1744 // TODO: We can optimize this by not scheduling the callback earlier. Since we1745 // currently schedule the callback in multiple places, will wait until those1746 // are consolidated.1747 if (1748 includesSomeLane(pendingPassiveEffectsLanes, SyncLane) &&1749 root.tag !== LegacyRoot1750 ) {1751 flushPassiveEffects();1752 }1753 // If layout work was scheduled, flush it now.1754 flushSyncCallbacks();1755 if (__DEV__) {1756 if (enableDebugTracing) {1757 logCommitStopped();1758 }1759 }1760 if (enableSchedulingProfiler) {1761 markCommitStopped();1762 }1763 return null;1764}1765export function flushPassiveEffects(): boolean {1766 // Returns whether passive effects were flushed.1767 // TODO: Combine this check with the one in flushPassiveEFfectsImpl. We should1768 // probably just combine the two functions. I believe they were only separate1769 // in the first place because we used to wrap it with1770 // `Scheduler.runWithPriority`, which accepts a function. But now we track the1771 // priority within React itself, so we can mutate the variable directly.1772 if (rootWithPendingPassiveEffects !== null) {1773 const renderPriority = lanesToEventPriority(pendingPassiveEffectsLanes);1774 const priority = lowerEventPriority(DefaultEventPriority, renderPriority);1775 const prevTransition = ReactCurrentBatchConfig.transition;1776 const previousPriority = getCurrentUpdatePriority();1777 try {1778 ReactCurrentBatchConfig.transition = 0;1779 setCurrentUpdatePriority(priority);1780 return flushPassiveEffectsImpl();1781 } finally {1782 setCurrentUpdatePriority(previousPriority);1783 ReactCurrentBatchConfig.transition = prevTransition;1784 }1785 }1786 return false;1787}1788export function enqueuePendingPassiveProfilerEffect(fiber: Fiber): void {1789 if (enableProfilerTimer && enableProfilerCommitHooks) {1790 pendingPassiveProfilerEffects.push(fiber);1791 if (!rootDoesHavePassiveEffects) {1792 rootDoesHavePassiveEffects = true;1793 scheduleCallback(NormalSchedulerPriority, () => {1794 flushPassiveEffects();1795 return null;1796 });1797 }1798 }1799}1800function flushPassiveEffectsImpl() {1801 if (rootWithPendingPassiveEffects === null) {1802 return false;1803 }1804 const root = rootWithPendingPassiveEffects;1805 const lanes = pendingPassiveEffectsLanes;1806 rootWithPendingPassiveEffects = null;1807 // TODO: This is sometimes out of sync with rootWithPendingPassiveEffects.1808 // Figure out why and fix it. It's not causing any known issues (probably1809 // because it's only used for profiling), but it's a refactor hazard.1810 pendingPassiveEffectsLanes = NoLanes;1811 invariant(1812 (executionContext & (RenderContext | CommitContext)) === NoContext,1813 'Cannot flush passive effects while already rendering.',1814 );1815 if (__DEV__) {1816 if (enableDebugTracing) {1817 logPassiveEffectsStarted(lanes);1818 }1819 }1820 if (enableSchedulingProfiler) {1821 markPassiveEffectsStarted(lanes);1822 }1823 const prevExecutionContext = executionContext;1824 executionContext |= CommitContext;1825 commitPassiveUnmountEffects(root.current);1826 commitPassiveMountEffects(root, root.current);1827 // TODO: Move to commitPassiveMountEffects1828 if (enableProfilerTimer && enableProfilerCommitHooks) {1829 const profilerEffects = pendingPassiveProfilerEffects;1830 pendingPassiveProfilerEffects = [];1831 for (let i = 0; i < profilerEffects.length; i++) {1832 const fiber = ((profilerEffects[i]: any): Fiber);1833 commitPassiveEffectDurations(root, fiber);1834 }1835 }1836 if (__DEV__) {1837 if (enableDebugTracing) {1838 logPassiveEffectsStopped();1839 }1840 }1841 if (enableSchedulingProfiler) {1842 markPassiveEffectsStopped();1843 }1844 if (__DEV__ && enableStrictEffects) {1845 commitDoubleInvokeEffectsInDEV(root.current, true);1846 }1847 executionContext = prevExecutionContext;1848 flushSyncCallbacks();1849 // If additional passive effects were scheduled, increment a counter. If this1850 // exceeds the limit, we'll fire a warning.1851 nestedPassiveUpdateCount =1852 rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;1853 // TODO: Move to commitPassiveMountEffects1854 onPostCommitRootDevTools(root);1855 if (enableProfilerTimer && enableProfilerCommitHooks) {1856 const stateNode = root.current.stateNode;1857 stateNode.effectDuration = 0;1858 stateNode.passiveEffectDuration = 0;1859 }1860 return true;1861}1862export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {1863 return (1864 legacyErrorBoundariesThatAlreadyFailed !== null &&1865 legacyErrorBoundariesThatAlreadyFailed.has(instance)1866 );1867}1868export function markLegacyErrorBoundaryAsFailed(instance: mixed) {1869 if (legacyErrorBoundariesThatAlreadyFailed === null) {1870 legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);1871 } else {1872 legacyErrorBoundariesThatAlreadyFailed.add(instance);1873 }1874}1875function prepareToThrowUncaughtError(error: mixed) {1876 if (!hasUncaughtError) {1877 hasUncaughtError = true;1878 firstUncaughtError = error;1879 }1880}1881export const onUncaughtError = prepareToThrowUncaughtError;1882function captureCommitPhaseErrorOnRoot(1883 rootFiber: Fiber,1884 sourceFiber: Fiber,1885 error: mixed,1886) {1887 const errorInfo = createCapturedValue(error, sourceFiber);1888 const update = createRootErrorUpdate(rootFiber, errorInfo, (SyncLane: Lane));1889 enqueueUpdate(rootFiber, update, (SyncLane: Lane));1890 const eventTime = requestEventTime();1891 const root = markUpdateLaneFromFiberToRoot(rootFiber, (SyncLane: Lane));1892 if (root !== null) {1893 markRootUpdated(root, SyncLane, eventTime);1894 ensureRootIsScheduled(root, eventTime);1895 }1896}1897export function captureCommitPhaseError(1898 sourceFiber: Fiber,1899 nearestMountedAncestor: Fiber | null,1900 error: mixed,1901) {1902 if (sourceFiber.tag === HostRoot) {1903 // Error was thrown at the root. There is no parent, so the root1904 // itself should capture it.1905 captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);1906 return;1907 }1908 let fiber = null;1909 if (skipUnmountedBoundaries) {1910 fiber = nearestMountedAncestor;1911 } else {1912 fiber = sourceFiber.return;1913 }1914 while (fiber !== null) {1915 if (fiber.tag === HostRoot) {1916 captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);1917 return;1918 } else if (fiber.tag === ClassComponent) {1919 const ctor = fiber.type;1920 const instance = fiber.stateNode;1921 if (1922 typeof ctor.getDerivedStateFromError === 'function' ||1923 (typeof instance.componentDidCatch === 'function' &&1924 !isAlreadyFailedLegacyErrorBoundary(instance))1925 ) {1926 const errorInfo = createCapturedValue(error, sourceFiber);1927 const update = createClassErrorUpdate(1928 fiber,1929 errorInfo,1930 (SyncLane: Lane),1931 );1932 enqueueUpdate(fiber, update, (SyncLane: Lane));1933 const eventTime = requestEventTime();1934 const root = markUpdateLaneFromFiberToRoot(fiber, (SyncLane: Lane));1935 if (root !== null) {1936 markRootUpdated(root, SyncLane, eventTime);1937 ensureRootIsScheduled(root, eventTime);1938 }1939 return;1940 }1941 }1942 fiber = fiber.return;1943 }1944 if (__DEV__) {1945 // TODO: Until we re-land skipUnmountedBoundaries (see #20147), this warning1946 // will fire for errors that are thrown by destroy functions inside deleted1947 // trees. What it should instead do is propagate the error to the parent of1948 // the deleted tree. In the meantime, do not add this warning to the1949 // allowlist; this is only for our internal use.1950 console.error(1951 'Internal React error: Attempted to capture a commit phase error ' +1952 'inside a detached tree. This indicates a bug in React. Likely ' +1953 'causes include deleting the same fiber more than once, committing an ' +1954 'already-finished tree, or an inconsistent return pointer.\n\n' +1955 'Error message:\n\n%s',1956 error,1957 );1958 }1959}1960export function pingSuspendedRoot(1961 root: FiberRoot,1962 wakeable: Wakeable,1963 pingedLanes: Lanes,1964) {1965 const pingCache = root.pingCache;1966 if (pingCache !== null) {1967 // The wakeable resolved, so we no longer need to memoize, because it will1968 // never be thrown again.1969 pingCache.delete(wakeable);1970 }1971 const eventTime = requestEventTime();1972 markRootPinged(root, pingedLanes, eventTime);1973 if (1974 workInProgressRoot === root &&1975 isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)1976 ) {1977 // Received a ping at the same priority level at which we're currently1978 // rendering. We might want to restart this render. This should mirror1979 // the logic of whether or not a root suspends once it completes.1980 // TODO: If we're rendering sync either due to Sync, Batched or expired,1981 // we should probably never restart.1982 // If we're suspended with delay, or if it's a retry, we'll always suspend1983 // so we can always restart.1984 if (1985 workInProgressRootExitStatus === RootSuspendedWithDelay ||1986 (workInProgressRootExitStatus === RootSuspended &&1987 includesOnlyRetries(workInProgressRootRenderLanes) &&1988 now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS)1989 ) {1990 // Restart from the root.1991 prepareFreshStack(root, NoLanes);1992 } else {1993 // Even though we can't restart right now, we might get an1994 // opportunity later. So we mark this render as having a ping.1995 workInProgressRootPingedLanes = mergeLanes(1996 workInProgressRootPingedLanes,1997 pingedLanes,1998 );1999 }2000 }2001 ensureRootIsScheduled(root, eventTime);2002}2003function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {2004 // The boundary fiber (a Suspense component or SuspenseList component)2005 // previously was rendered in its fallback state. One of the promises that2006 // suspended it has resolved, which means at least part of the tree was2007 // likely unblocked. Try rendering again, at a new lanes.2008 if (retryLane === NoLane) {2009 // TODO: Assign this to `suspenseState.retryLane`? to avoid2010 // unnecessary entanglement?2011 retryLane = requestRetryLane(boundaryFiber);2012 }2013 // TODO: Special case idle priority?2014 const eventTime = requestEventTime();2015 const root = markUpdateLaneFromFiberToRoot(boundaryFiber, retryLane);2016 if (root !== null) {2017 markRootUpdated(root, retryLane, eventTime);2018 ensureRootIsScheduled(root, eventTime);2019 }2020}2021export function retryDehydratedSuspenseBoundary(boundaryFiber: Fiber) {2022 const suspenseState: null | SuspenseState = boundaryFiber.memoizedState;2023 let retryLane = NoLane;2024 if (suspenseState !== null) {2025 retryLane = suspenseState.retryLane;2026 }2027 retryTimedOutBoundary(boundaryFiber, retryLane);2028}2029export function resolveRetryWakeable(boundaryFiber: Fiber, wakeable: Wakeable) {2030 let retryLane = NoLane; // Default2031 let retryCache: WeakSet<Wakeable> | Set<Wakeable> | null;2032 if (enableSuspenseServerRenderer) {2033 switch (boundaryFiber.tag) {2034 case SuspenseComponent:2035 retryCache = boundaryFiber.stateNode;2036 const suspenseState: null | SuspenseState = boundaryFiber.memoizedState;2037 if (suspenseState !== null) {2038 retryLane = suspenseState.retryLane;2039 }2040 break;2041 case SuspenseListComponent:2042 retryCache = boundaryFiber.stateNode;2043 break;2044 default:2045 invariant(2046 false,2047 'Pinged unknown suspense boundary type. ' +2048 'This is probably a bug in React.',2049 );2050 }2051 } else {2052 retryCache = boundaryFiber.stateNode;2053 }2054 if (retryCache !== null) {2055 // The wakeable resolved, so we no longer need to memoize, because it will2056 // never be thrown again.2057 retryCache.delete(wakeable);2058 }2059 retryTimedOutBoundary(boundaryFiber, retryLane);2060}2061// Computes the next Just Noticeable Difference (JND) boundary.2062// The theory is that a person can't tell the difference between small differences in time.2063// Therefore, if we wait a bit longer than necessary that won't translate to a noticeable2064// difference in the experience. However, waiting for longer might mean that we can avoid2065// showing an intermediate loading state. The longer we have already waited, the harder it2066// is to tell small differences in time. Therefore, the longer we've already waited,2067// the longer we can wait additionally. At some point we have to give up though.2068// We pick a train model where the next boundary commits at a consistent schedule.2069// These particular numbers are vague estimates. We expect to adjust them based on research.2070function jnd(timeElapsed: number) {2071 return timeElapsed < 1202072 ? 1202073 : timeElapsed < 4802074 ? 4802075 : timeElapsed < 10802076 ? 10802077 : timeElapsed < 19202078 ? 19202079 : timeElapsed < 30002080 ? 30002081 : timeElapsed < 43202082 ? 43202083 : ceil(timeElapsed / 1960) * 1960;2084}2085function checkForNestedUpdates() {2086 if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {2087 nestedUpdateCount = 0;2088 rootWithNestedUpdates = null;2089 invariant(2090 false,2091 'Maximum update depth exceeded. This can happen when a component ' +2092 'repeatedly calls setState inside componentWillUpdate or ' +2093 'componentDidUpdate. React limits the number of nested updates to ' +2094 'prevent infinite loops.',2095 );2096 }2097 if (__DEV__) {2098 if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {2099 nestedPassiveUpdateCount = 0;2100 console.error(2101 'Maximum update depth exceeded. This can happen when a component ' +2102 "calls setState inside useEffect, but useEffect either doesn't " +2103 'have a dependency array, or one of the dependencies changes on ' +2104 'every render.',2105 );2106 }2107 }2108}2109function flushRenderPhaseStrictModeWarningsInDEV() {2110 if (__DEV__) {2111 ReactStrictModeWarnings.flushLegacyContextWarning();2112 if (warnAboutDeprecatedLifecycles) {2113 ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();2114 }2115 }2116}2117function commitDoubleInvokeEffectsInDEV(2118 fiber: Fiber,2119 hasPassiveEffects: boolean,2120) {2121 if (__DEV__ && enableStrictEffects) {2122 // TODO (StrictEffects) Should we set a marker on the root if it contains strict effects2123 // so we don't traverse unnecessarily? similar to subtreeFlags but just at the root level.2124 // Maybe not a big deal since this is DEV only behavior.2125 setCurrentDebugFiberInDEV(fiber);2126 invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectUnmountInDEV);2127 if (hasPassiveEffects) {2128 invokeEffectsInDev(2129 fiber,2130 MountPassiveDev,2131 invokePassiveEffectUnmountInDEV,2132 );2133 }2134 invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectMountInDEV);2135 if (hasPassiveEffects) {2136 invokeEffectsInDev(fiber, MountPassiveDev, invokePassiveEffectMountInDEV);2137 }2138 resetCurrentDebugFiberInDEV();2139 }2140}2141function invokeEffectsInDev(2142 firstChild: Fiber,2143 fiberFlags: Flags,2144 invokeEffectFn: (fiber: Fiber) => void,2145): void {2146 if (__DEV__ && enableStrictEffects) {2147 // We don't need to re-check StrictEffectsMode here.2148 // This function is only called if that check has already passed.2149 let current = firstChild;2150 let subtreeRoot = null;2151 while (current !== null) {2152 const primarySubtreeFlag = current.subtreeFlags & fiberFlags;2153 if (2154 current !== subtreeRoot &&2155 current.child !== null &&2156 primarySubtreeFlag !== NoFlags2157 ) {2158 current = current.child;2159 } else {2160 if ((current.flags & fiberFlags) !== NoFlags) {2161 invokeEffectFn(current);2162 }2163 if (current.sibling !== null) {2164 current = current.sibling;2165 } else {2166 current = subtreeRoot = current.return;2167 }2168 }2169 }2170 }2171}2172let didWarnStateUpdateForNotYetMountedComponent: Set<string> | null = null;2173function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber) {2174 if (__DEV__) {2175 if ((executionContext & RenderContext) !== NoContext) {2176 // We let the other warning about render phase updates deal with this one.2177 return;2178 }2179 if (!(fiber.mode & ConcurrentMode)) {2180 return;2181 }2182 const tag = fiber.tag;2183 if (2184 tag !== IndeterminateComponent &&2185 tag !== HostRoot &&2186 tag !== ClassComponent &&2187 tag !== FunctionComponent &&2188 tag !== ForwardRef &&2189 tag !== MemoComponent &&2190 tag !== SimpleMemoComponent2191 ) {2192 // Only warn for user-defined components, not internal ones like Suspense.2193 return;2194 }2195 // We show the whole stack but dedupe on the top component's name because2196 // the problematic code almost always lies inside that component.2197 const componentName = getComponentNameFromFiber(fiber) || 'ReactComponent';2198 if (didWarnStateUpdateForNotYetMountedComponent !== null) {2199 if (didWarnStateUpdateForNotYetMountedComponent.has(componentName)) {2200 return;2201 }2202 didWarnStateUpdateForNotYetMountedComponent.add(componentName);2203 } else {2204 didWarnStateUpdateForNotYetMountedComponent = new Set([componentName]);2205 }2206 const previousFiber = ReactCurrentFiberCurrent;2207 try {2208 setCurrentDebugFiberInDEV(fiber);2209 console.error(2210 "Can't perform a React state update on a component that hasn't mounted yet. " +2211 'This indicates that you have a side-effect in your render function that ' +2212 'asynchronously later calls tries to update the component. Move this work to ' +2213 'useEffect instead.',2214 );2215 } finally {2216 if (previousFiber) {2217 setCurrentDebugFiberInDEV(fiber);2218 } else {2219 resetCurrentDebugFiberInDEV();2220 }2221 }2222 }2223}2224let beginWork;2225if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {2226 const dummyFiber = null;2227 beginWork = (current, unitOfWork, lanes) => {2228 // If a component throws an error, we replay it again in a synchronously2229 // dispatched event, so that the debugger will treat it as an uncaught2230 // error See ReactErrorUtils for more information.2231 // Before entering the begin phase, copy the work-in-progress onto a dummy2232 // fiber. If beginWork throws, we'll use this to reset the state.2233 const originalWorkInProgressCopy = assignFiberPropertiesInDEV(2234 dummyFiber,2235 unitOfWork,2236 );2237 try {2238 return originalBeginWork(current, unitOfWork, lanes);2239 } catch (originalError) {2240 if (2241 originalError !== null &&2242 typeof originalError === 'object' &&2243 typeof originalError.then === 'function'2244 ) {2245 // Don't replay promises. Treat everything else like an error.2246 throw originalError;2247 }2248 // Keep this code in sync with handleError; any changes here must have2249 // corresponding changes there.2250 resetContextDependencies();2251 resetHooksAfterThrow();2252 // Don't reset current debug fiber, since we're about to work on the2253 // same fiber again.2254 // Unwind the failed stack frame2255 unwindInterruptedWork(unitOfWork, workInProgressRootRenderLanes);2256 // Restore the original properties of the fiber.2257 assignFiberPropertiesInDEV(unitOfWork, originalWorkInProgressCopy);2258 if (enableProfilerTimer && unitOfWork.mode & ProfileMode) {2259 // Reset the profiler timer.2260 startProfilerTimer(unitOfWork);2261 }2262 // Run beginWork again.2263 invokeGuardedCallback(2264 null,2265 originalBeginWork,...
ReactFiberWorkLoop.old.js
Source:ReactFiberWorkLoop.old.js
...1114 let erroredWork = workInProgress;1115 try {1116 // Reset module-level state that was set during the render phase.1117 resetContextDependencies();1118 resetHooksAfterThrow();1119 resetCurrentDebugFiberInDEV();1120 // TODO: I found and added this missing line while investigating a1121 // separate issue. Write a regression test using string refs.1122 ReactCurrentOwner.current = null;1123 if (erroredWork === null || erroredWork.return === null) {1124 // Expected to be working on a non-root fiber. This is a fatal error1125 // because there's no ancestor that can handle it; the root is1126 // supposed to capture all errors that weren't caught by an error1127 // boundary.1128 workInProgressRootExitStatus = RootFatalErrored;1129 workInProgressRootFatalError = thrownValue;1130 // Set `workInProgress` to null. This represents advancing to the next1131 // sibling, or the parent if there are no siblings. But since the root1132 // has no siblings nor a parent, we set it to null. Usually this is1133 // handled by `completeUnitOfWork` or `unwindWork`, but since we're1134 // intentionally not calling those, we need set it here.1135 // TODO: Consider calling `unwindWork` to pop the contexts.1136 workInProgress = null;1137 return;1138 }1139 if (enableProfilerTimer && erroredWork.mode & ProfileMode) {1140 // Record the time spent rendering before an error was thrown. This1141 // avoids inaccurate Profiler durations in the case of a1142 // suspended render.1143 stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true);1144 }1145 throwException(1146 root,1147 erroredWork.return,1148 erroredWork,1149 thrownValue,1150 workInProgressRootRenderLanes,1151 );1152 completeUnitOfWork(erroredWork);1153 } catch (yetAnotherThrownValue) {1154 // Something in the return path also threw.1155 thrownValue = yetAnotherThrownValue;1156 if (workInProgress === erroredWork && erroredWork !== null) {1157 // If this boundary has already errored, then we had trouble processing1158 // the error. Bubble it to the next boundary.1159 erroredWork = erroredWork.return;1160 workInProgress = erroredWork;1161 } else {1162 erroredWork = workInProgress;1163 }1164 continue;1165 }1166 // Return to the normal work loop.1167 return;1168 } while (true);1169}1170function pushDispatcher() {1171 const prevDispatcher = ReactCurrentDispatcher.current;1172 ReactCurrentDispatcher.current = ContextOnlyDispatcher;1173 if (prevDispatcher === null) {1174 // The React isomorphic package does not include a default dispatcher.1175 // Instead the first renderer will lazily attach one, in order to give1176 // nicer error messages.1177 return ContextOnlyDispatcher;1178 } else {1179 return prevDispatcher;1180 }1181}1182function popDispatcher(prevDispatcher) {1183 ReactCurrentDispatcher.current = prevDispatcher;1184}1185export function markCommitTimeOfFallback() {1186 globalMostRecentFallbackTime = now();1187}1188export function markSkippedUpdateLanes(lane: Lane | Lanes): void {1189 workInProgressRootSkippedLanes = mergeLanes(1190 lane,1191 workInProgressRootSkippedLanes,1192 );1193}1194export function renderDidSuspend(): void {1195 if (workInProgressRootExitStatus === RootIncomplete) {1196 workInProgressRootExitStatus = RootSuspended;1197 }1198}1199export function renderDidSuspendDelayIfPossible(): void {1200 if (1201 workInProgressRootExitStatus === RootIncomplete ||1202 workInProgressRootExitStatus === RootSuspended1203 ) {1204 workInProgressRootExitStatus = RootSuspendedWithDelay;1205 }1206 // Check if there are updates that we skipped tree that might have unblocked1207 // this render.1208 if (1209 workInProgressRoot !== null &&1210 (includesNonIdleWork(workInProgressRootSkippedLanes) ||1211 includesNonIdleWork(workInProgressRootUpdatedLanes))1212 ) {1213 // Mark the current render as suspended so that we switch to working on1214 // the updates that were skipped. Usually we only suspend at the end of1215 // the render phase.1216 // TODO: We should probably always mark the root as suspended immediately1217 // (inside this function), since by suspending at the end of the render1218 // phase introduces a potential mistake where we suspend lanes that were1219 // pinged or updated while we were rendering.1220 markRootSuspended(workInProgressRoot, workInProgressRootRenderLanes);1221 }1222}1223export function renderDidError() {1224 if (workInProgressRootExitStatus !== RootCompleted) {1225 workInProgressRootExitStatus = RootErrored;1226 }1227}1228// Called during render to determine if anything has suspended.1229// Returns false if we're not sure.1230export function renderHasNotSuspendedYet(): boolean {1231 // If something errored or completed, we can't really be sure,1232 // so those are false.1233 return workInProgressRootExitStatus === RootIncomplete;1234}1235function renderRootSync(root: FiberRoot, lanes: Lanes) {1236 const prevExecutionContext = executionContext;1237 executionContext |= RenderContext;1238 const prevDispatcher = pushDispatcher();1239 // If the root or lanes have changed, throw out the existing stack1240 // and prepare a fresh one. Otherwise we'll continue where we left off.1241 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1242 if (enableUpdaterTracking) {1243 if (isDevToolsPresent) {1244 const memoizedUpdaters = root.memoizedUpdaters;1245 if (memoizedUpdaters.size > 0) {1246 restorePendingUpdaters(root, workInProgressRootRenderLanes);1247 memoizedUpdaters.clear();1248 }1249 // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.1250 // If we bailout on this work, we'll move them back (like above).1251 // It's important to move them now in case the work spawns more work at the same priority with different updaters.1252 // That way we can keep the current update and future updates separate.1253 movePendingFibersToMemoized(root, lanes);1254 }1255 }1256 prepareFreshStack(root, lanes);1257 }1258 if (__DEV__) {1259 if (enableDebugTracing) {1260 logRenderStarted(lanes);1261 }1262 }1263 if (enableSchedulingProfiler) {1264 markRenderStarted(lanes);1265 }1266 do {1267 try {1268 workLoopSync();1269 break;1270 } catch (thrownValue) {1271 handleError(root, thrownValue);1272 }1273 } while (true);1274 resetContextDependencies();1275 executionContext = prevExecutionContext;1276 popDispatcher(prevDispatcher);1277 if (workInProgress !== null) {1278 // This is a sync render, so we should have finished the whole tree.1279 invariant(1280 false,1281 'Cannot commit an incomplete root. This error is likely caused by a ' +1282 'bug in React. Please file an issue.',1283 );1284 }1285 if (__DEV__) {1286 if (enableDebugTracing) {1287 logRenderStopped();1288 }1289 }1290 if (enableSchedulingProfiler) {1291 markRenderStopped();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 // Already timed out, so perform work without checking if we need to yield.1302 while (workInProgress !== null) {1303 performUnitOfWork(workInProgress);1304 }1305}1306function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {1307 const prevExecutionContext = executionContext;1308 executionContext |= RenderContext;1309 const prevDispatcher = pushDispatcher();1310 // If the root or lanes have changed, throw out the existing stack1311 // and prepare a fresh one. Otherwise we'll continue where we left off.1312 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {1313 if (enableUpdaterTracking) {1314 if (isDevToolsPresent) {1315 const memoizedUpdaters = root.memoizedUpdaters;1316 if (memoizedUpdaters.size > 0) {1317 restorePendingUpdaters(root, workInProgressRootRenderLanes);1318 memoizedUpdaters.clear();1319 }1320 // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.1321 // If we bailout on this work, we'll move them back (like above).1322 // It's important to move them now in case the work spawns more work at the same priority with different updaters.1323 // That way we can keep the current update and future updates separate.1324 movePendingFibersToMemoized(root, lanes);1325 }1326 }1327 resetRenderTimer();1328 prepareFreshStack(root, lanes);1329 }1330 if (__DEV__) {1331 if (enableDebugTracing) {1332 logRenderStarted(lanes);1333 }1334 }1335 if (enableSchedulingProfiler) {1336 markRenderStarted(lanes);1337 }1338 do {1339 try {1340 workLoopConcurrent();1341 break;1342 } catch (thrownValue) {1343 handleError(root, thrownValue);1344 }1345 } while (true);1346 resetContextDependencies();1347 popDispatcher(prevDispatcher);1348 executionContext = prevExecutionContext;1349 if (__DEV__) {1350 if (enableDebugTracing) {1351 logRenderStopped();1352 }1353 }1354 // Check if the tree has completed.1355 if (workInProgress !== null) {1356 // Still work remaining.1357 if (enableSchedulingProfiler) {1358 markRenderYielded();1359 }1360 return RootIncomplete;1361 } else {1362 // Completed the tree.1363 if (enableSchedulingProfiler) {1364 markRenderStopped();1365 }1366 // Set this to null to indicate there's no in-progress render.1367 workInProgressRoot = null;1368 workInProgressRootRenderLanes = NoLanes;1369 // Return the final exit status.1370 return workInProgressRootExitStatus;1371 }1372}1373/** @noinline */1374function workLoopConcurrent() {1375 // Perform work until Scheduler asks us to yield1376 while (workInProgress !== null && !shouldYield()) {1377 performUnitOfWork(workInProgress);1378 }1379}1380function performUnitOfWork(unitOfWork: Fiber): void {1381 // 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 setCurrentDebugFiberInDEV(unitOfWork);1386 let next;1387 if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {1388 startProfilerTimer(unitOfWork);1389 next = beginWork(current, unitOfWork, subtreeRenderLanes);1390 stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);1391 } else {1392 next = beginWork(current, unitOfWork, subtreeRenderLanes);1393 }1394 resetCurrentDebugFiberInDEV();1395 unitOfWork.memoizedProps = unitOfWork.pendingProps;1396 if (next === null) {1397 // If this doesn't spawn new work, complete the current work.1398 completeUnitOfWork(unitOfWork);1399 } else {1400 workInProgress = next;1401 }1402 ReactCurrentOwner.current = null;1403}1404function completeUnitOfWork(unitOfWork: Fiber): void {1405 // Attempt to complete the current unit of work, then move to the next1406 // sibling. If there are no more siblings, return to the parent fiber.1407 let completedWork = unitOfWork;1408 do {1409 // The current, flushed, state of this fiber is the alternate. Ideally1410 // nothing should rely on this, but relying on it here means that we don't1411 // need an additional field on the work in progress.1412 const current = completedWork.alternate;1413 const returnFiber = completedWork.return;1414 // Check if the work completed or if something threw.1415 if ((completedWork.flags & Incomplete) === NoFlags) {1416 setCurrentDebugFiberInDEV(completedWork);1417 let next;1418 if (1419 !enableProfilerTimer ||1420 (completedWork.mode & ProfileMode) === NoMode1421 ) {1422 next = completeWork(current, completedWork, subtreeRenderLanes);1423 } else {1424 startProfilerTimer(completedWork);1425 next = completeWork(current, completedWork, subtreeRenderLanes);1426 // Update render duration assuming we didn't error.1427 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1428 }1429 resetCurrentDebugFiberInDEV();1430 if (next !== null) {1431 // Completing this fiber spawned new work. Work on that next.1432 workInProgress = next;1433 return;1434 }1435 } else {1436 // This fiber did not complete because something threw. Pop values off1437 // the stack without entering the complete phase. If this is a boundary,1438 // capture values if possible.1439 const next = unwindWork(completedWork, subtreeRenderLanes);1440 // Because this fiber did not complete, don't reset its lanes.1441 if (next !== null) {1442 // If completing this work spawned new work, do that next. We'll come1443 // back here again.1444 // Since we're restarting, remove anything that is not a host effect1445 // from the effect tag.1446 next.flags &= HostEffectMask;1447 workInProgress = next;1448 return;1449 }1450 if (1451 enableProfilerTimer &&1452 (completedWork.mode & ProfileMode) !== NoMode1453 ) {1454 // Record the render duration for the fiber that errored.1455 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);1456 // Include the time spent working on failed children before continuing.1457 let actualDuration = completedWork.actualDuration;1458 let child = completedWork.child;1459 while (child !== null) {1460 actualDuration += child.actualDuration;1461 child = child.sibling;1462 }1463 completedWork.actualDuration = actualDuration;1464 }1465 if (returnFiber !== null) {1466 // Mark the parent fiber as incomplete and clear its subtree flags.1467 returnFiber.flags |= Incomplete;1468 returnFiber.subtreeFlags = NoFlags;1469 returnFiber.deletions = null;1470 }1471 }1472 const siblingFiber = completedWork.sibling;1473 if (siblingFiber !== null) {1474 // If there is more work to do in this returnFiber, do that next.1475 workInProgress = siblingFiber;1476 return;1477 }1478 // Otherwise, return to the parent1479 completedWork = returnFiber;1480 // Update the next thing we're working on in case something throws.1481 workInProgress = completedWork;1482 } while (completedWork !== null);1483 // We've reached the root.1484 if (workInProgressRootExitStatus === RootIncomplete) {1485 workInProgressRootExitStatus = RootCompleted;1486 }1487}1488function commitRoot(root) {1489 // TODO: This no longer makes any sense. We already wrap the mutation and1490 // layout phases. Should be able to remove.1491 const previousUpdateLanePriority = getCurrentUpdatePriority();1492 const prevTransition = ReactCurrentBatchConfig.transition;1493 try {1494 ReactCurrentBatchConfig.transition = 0;1495 setCurrentUpdatePriority(DiscreteEventPriority);1496 commitRootImpl(root, previousUpdateLanePriority);1497 } finally {1498 ReactCurrentBatchConfig.transition = prevTransition;1499 setCurrentUpdatePriority(previousUpdateLanePriority);1500 }1501 return null;1502}1503function commitRootImpl(root, renderPriorityLevel) {1504 do {1505 // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which1506 // means `flushPassiveEffects` will sometimes result in additional1507 // passive effects. So we need to keep flushing in a loop until there are1508 // no more pending effects.1509 // TODO: Might be better if `flushPassiveEffects` did not automatically1510 // flush synchronous work at the end, to avoid factoring hazards like this.1511 flushPassiveEffects();1512 } while (rootWithPendingPassiveEffects !== null);1513 flushRenderPhaseStrictModeWarningsInDEV();1514 invariant(1515 (executionContext & (RenderContext | CommitContext)) === NoContext,1516 'Should not already be working.',1517 );1518 const finishedWork = root.finishedWork;1519 const lanes = root.finishedLanes;1520 if (__DEV__) {1521 if (enableDebugTracing) {1522 logCommitStarted(lanes);1523 }1524 }1525 if (enableSchedulingProfiler) {1526 markCommitStarted(lanes);1527 }1528 if (finishedWork === null) {1529 if (__DEV__) {1530 if (enableDebugTracing) {1531 logCommitStopped();1532 }1533 }1534 if (enableSchedulingProfiler) {1535 markCommitStopped();1536 }1537 return null;1538 } else {1539 if (__DEV__) {1540 if (lanes === NoLanes) {1541 console.error(1542 'root.finishedLanes should not be empty during a commit. This is a ' +1543 'bug in React.',1544 );1545 }1546 }1547 }1548 root.finishedWork = null;1549 root.finishedLanes = NoLanes;1550 invariant(1551 finishedWork !== root.current,1552 'Cannot commit the same tree as before. This error is likely caused by ' +1553 'a bug in React. Please file an issue.',1554 );1555 // commitRoot never returns a continuation; it always finishes synchronously.1556 // So we can clear these now to allow a new callback to be scheduled.1557 root.callbackNode = null;1558 root.callbackPriority = NoLane;1559 // Update the first and last pending times on this root. The new first1560 // pending time is whatever is left on the root fiber.1561 let remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);1562 markRootFinished(root, remainingLanes);1563 if (root === workInProgressRoot) {1564 // We can reset these now that they are finished.1565 workInProgressRoot = null;1566 workInProgress = null;1567 workInProgressRootRenderLanes = NoLanes;1568 } else {1569 // This indicates that the last root we worked on is not the same one that1570 // we're committing now. This most commonly happens when a suspended root1571 // times out.1572 }1573 // If there are pending passive effects, schedule a callback to process them.1574 // Do this as early as possible, so it is queued before anything else that1575 // might get scheduled in the commit phase. (See #16714.)1576 // TODO: Delete all other places that schedule the passive effect callback1577 // They're redundant.1578 if (1579 (finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||1580 (finishedWork.flags & PassiveMask) !== NoFlags1581 ) {1582 if (!rootDoesHavePassiveEffects) {1583 rootDoesHavePassiveEffects = true;1584 scheduleCallback(NormalSchedulerPriority, () => {1585 flushPassiveEffects();1586 return null;1587 });1588 }1589 }1590 // Check if there are any effects in the whole tree.1591 // TODO: This is left over from the effect list implementation, where we had1592 // to check for the existence of `firstEffect` to satisfy Flow. I think the1593 // only other reason this optimization exists is because it affects profiling.1594 // Reconsider whether this is necessary.1595 const subtreeHasEffects =1596 (finishedWork.subtreeFlags &1597 (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1598 NoFlags;1599 const rootHasEffect =1600 (finishedWork.flags &1601 (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==1602 NoFlags;1603 if (subtreeHasEffects || rootHasEffect) {1604 const prevTransition = ReactCurrentBatchConfig.transition;1605 ReactCurrentBatchConfig.transition = 0;1606 const previousPriority = getCurrentUpdatePriority();1607 setCurrentUpdatePriority(DiscreteEventPriority);1608 const prevExecutionContext = executionContext;1609 executionContext |= CommitContext;1610 // Reset this to null before calling lifecycles1611 ReactCurrentOwner.current = null;1612 // The commit phase is broken into several sub-phases. We do a separate pass1613 // of the effect list for each phase: all mutation effects come before all1614 // layout effects, and so on.1615 // The first phase a "before mutation" phase. We use this phase to read the1616 // state of the host tree right before we mutate it. This is where1617 // getSnapshotBeforeUpdate is called.1618 const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(1619 root,1620 finishedWork,1621 );1622 if (enableProfilerTimer) {1623 // Mark the current commit time to be shared by all Profilers in this1624 // batch. This enables them to be grouped later.1625 recordCommitTime();1626 }1627 if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {1628 // Track the root here, rather than in commitLayoutEffects(), because of ref setters.1629 // Updates scheduled during ref detachment should also be flagged.1630 rootCommittingMutationOrLayoutEffects = root;1631 }1632 // The next phase is the mutation phase, where we mutate the host tree.1633 commitMutationEffects(root, finishedWork, lanes);1634 if (enableCreateEventHandleAPI) {1635 if (shouldFireAfterActiveInstanceBlur) {1636 afterActiveInstanceBlur();1637 }1638 }1639 resetAfterCommit(root.containerInfo);1640 // The work-in-progress tree is now the current tree. This must come after1641 // the mutation phase, so that the previous tree is still current during1642 // componentWillUnmount, but before the layout phase, so that the finished1643 // work is current during componentDidMount/Update.1644 root.current = finishedWork;1645 // The next phase is the layout phase, where we call effects that read1646 // the host tree after it's been mutated. The idiomatic use case for this is1647 // layout, but class component lifecycles also fire here for legacy reasons.1648 if (__DEV__) {1649 if (enableDebugTracing) {1650 logLayoutEffectsStarted(lanes);1651 }1652 }1653 if (enableSchedulingProfiler) {1654 markLayoutEffectsStarted(lanes);1655 }1656 commitLayoutEffects(finishedWork, root, lanes);1657 if (__DEV__) {1658 if (enableDebugTracing) {1659 logLayoutEffectsStopped();1660 }1661 }1662 if (enableSchedulingProfiler) {1663 markLayoutEffectsStopped();1664 }1665 if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {1666 rootCommittingMutationOrLayoutEffects = null;1667 }1668 // Tell Scheduler to yield at the end of the frame, so the browser has an1669 // opportunity to paint.1670 requestPaint();1671 executionContext = prevExecutionContext;1672 // Reset the priority to the previous non-sync value.1673 setCurrentUpdatePriority(previousPriority);1674 ReactCurrentBatchConfig.transition = prevTransition;1675 } else {1676 // No effects.1677 root.current = finishedWork;1678 // Measure these anyway so the flamegraph explicitly shows that there were1679 // no effects.1680 // TODO: Maybe there's a better way to report this.1681 if (enableProfilerTimer) {1682 recordCommitTime();1683 }1684 }1685 const rootDidHavePassiveEffects = rootDoesHavePassiveEffects;1686 if (rootDoesHavePassiveEffects) {1687 // This commit has passive effects. Stash a reference to them. But don't1688 // schedule a callback until after flushing layout work.1689 rootDoesHavePassiveEffects = false;1690 rootWithPendingPassiveEffects = root;1691 pendingPassiveEffectsLanes = lanes;1692 }1693 // Read this again, since an effect might have updated it1694 remainingLanes = root.pendingLanes;1695 // Check if there's remaining work on this root1696 if (remainingLanes === NoLanes) {1697 // If there's no remaining work, we can clear the set of already failed1698 // error boundaries.1699 legacyErrorBoundariesThatAlreadyFailed = null;1700 }1701 if (__DEV__ && enableStrictEffects) {1702 if (!rootDidHavePassiveEffects) {1703 commitDoubleInvokeEffectsInDEV(root.current, false);1704 }1705 }1706 if (includesSomeLane(remainingLanes, (SyncLane: Lane))) {1707 if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {1708 markNestedUpdateScheduled();1709 }1710 // Count the number of times the root synchronously re-renders without1711 // finishing. If there are too many, it indicates an infinite update loop.1712 if (root === rootWithNestedUpdates) {1713 nestedUpdateCount++;1714 } else {1715 nestedUpdateCount = 0;1716 rootWithNestedUpdates = root;1717 }1718 } else {1719 nestedUpdateCount = 0;1720 }1721 onCommitRootDevTools(finishedWork.stateNode, renderPriorityLevel);1722 if (enableUpdaterTracking) {1723 if (isDevToolsPresent) {1724 root.memoizedUpdaters.clear();1725 }1726 }1727 if (__DEV__) {1728 onCommitRootTestSelector();1729 }1730 // Always call this before exiting `commitRoot`, to ensure that any1731 // additional work on this root is scheduled.1732 ensureRootIsScheduled(root, now());1733 if (hasUncaughtError) {1734 hasUncaughtError = false;1735 const error = firstUncaughtError;1736 firstUncaughtError = null;1737 throw error;1738 }1739 // If the passive effects are the result of a discrete render, flush them1740 // synchronously at the end of the current task so that the result is1741 // immediately observable. Otherwise, we assume that they are not1742 // order-dependent and do not need to be observed by external systems, so we1743 // can wait until after paint.1744 // TODO: We can optimize this by not scheduling the callback earlier. Since we1745 // currently schedule the callback in multiple places, will wait until those1746 // are consolidated.1747 if (1748 includesSomeLane(pendingPassiveEffectsLanes, SyncLane) &&1749 root.tag !== LegacyRoot1750 ) {1751 flushPassiveEffects();1752 }1753 // If layout work was scheduled, flush it now.1754 flushSyncCallbacks();1755 if (__DEV__) {1756 if (enableDebugTracing) {1757 logCommitStopped();1758 }1759 }1760 if (enableSchedulingProfiler) {1761 markCommitStopped();1762 }1763 return null;1764}1765export function flushPassiveEffects(): boolean {1766 // Returns whether passive effects were flushed.1767 // TODO: Combine this check with the one in flushPassiveEFfectsImpl. We should1768 // probably just combine the two functions. I believe they were only separate1769 // in the first place because we used to wrap it with1770 // `Scheduler.runWithPriority`, which accepts a function. But now we track the1771 // priority within React itself, so we can mutate the variable directly.1772 if (rootWithPendingPassiveEffects !== null) {1773 const renderPriority = lanesToEventPriority(pendingPassiveEffectsLanes);1774 const priority = lowerEventPriority(DefaultEventPriority, renderPriority);1775 const prevTransition = ReactCurrentBatchConfig.transition;1776 const previousPriority = getCurrentUpdatePriority();1777 try {1778 ReactCurrentBatchConfig.transition = 0;1779 setCurrentUpdatePriority(priority);1780 return flushPassiveEffectsImpl();1781 } finally {1782 setCurrentUpdatePriority(previousPriority);1783 ReactCurrentBatchConfig.transition = prevTransition;1784 }1785 }1786 return false;1787}1788export function enqueuePendingPassiveProfilerEffect(fiber: Fiber): void {1789 if (enableProfilerTimer && enableProfilerCommitHooks) {1790 pendingPassiveProfilerEffects.push(fiber);1791 if (!rootDoesHavePassiveEffects) {1792 rootDoesHavePassiveEffects = true;1793 scheduleCallback(NormalSchedulerPriority, () => {1794 flushPassiveEffects();1795 return null;1796 });1797 }1798 }1799}1800function flushPassiveEffectsImpl() {1801 if (rootWithPendingPassiveEffects === null) {1802 return false;1803 }1804 const root = rootWithPendingPassiveEffects;1805 const lanes = pendingPassiveEffectsLanes;1806 rootWithPendingPassiveEffects = null;1807 // TODO: This is sometimes out of sync with rootWithPendingPassiveEffects.1808 // Figure out why and fix it. It's not causing any known issues (probably1809 // because it's only used for profiling), but it's a refactor hazard.1810 pendingPassiveEffectsLanes = NoLanes;1811 invariant(1812 (executionContext & (RenderContext | CommitContext)) === NoContext,1813 'Cannot flush passive effects while already rendering.',1814 );1815 if (__DEV__) {1816 if (enableDebugTracing) {1817 logPassiveEffectsStarted(lanes);1818 }1819 }1820 if (enableSchedulingProfiler) {1821 markPassiveEffectsStarted(lanes);1822 }1823 const prevExecutionContext = executionContext;1824 executionContext |= CommitContext;1825 commitPassiveUnmountEffects(root.current);1826 commitPassiveMountEffects(root, root.current);1827 // TODO: Move to commitPassiveMountEffects1828 if (enableProfilerTimer && enableProfilerCommitHooks) {1829 const profilerEffects = pendingPassiveProfilerEffects;1830 pendingPassiveProfilerEffects = [];1831 for (let i = 0; i < profilerEffects.length; i++) {1832 const fiber = ((profilerEffects[i]: any): Fiber);1833 commitPassiveEffectDurations(root, fiber);1834 }1835 }1836 if (__DEV__) {1837 if (enableDebugTracing) {1838 logPassiveEffectsStopped();1839 }1840 }1841 if (enableSchedulingProfiler) {1842 markPassiveEffectsStopped();1843 }1844 if (__DEV__ && enableStrictEffects) {1845 commitDoubleInvokeEffectsInDEV(root.current, true);1846 }1847 executionContext = prevExecutionContext;1848 flushSyncCallbacks();1849 // If additional passive effects were scheduled, increment a counter. If this1850 // exceeds the limit, we'll fire a warning.1851 nestedPassiveUpdateCount =1852 rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;1853 // TODO: Move to commitPassiveMountEffects1854 onPostCommitRootDevTools(root);1855 if (enableProfilerTimer && enableProfilerCommitHooks) {1856 const stateNode = root.current.stateNode;1857 stateNode.effectDuration = 0;1858 stateNode.passiveEffectDuration = 0;1859 }1860 return true;1861}1862export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {1863 return (1864 legacyErrorBoundariesThatAlreadyFailed !== null &&1865 legacyErrorBoundariesThatAlreadyFailed.has(instance)1866 );1867}1868export function markLegacyErrorBoundaryAsFailed(instance: mixed) {1869 if (legacyErrorBoundariesThatAlreadyFailed === null) {1870 legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);1871 } else {1872 legacyErrorBoundariesThatAlreadyFailed.add(instance);1873 }1874}1875function prepareToThrowUncaughtError(error: mixed) {1876 if (!hasUncaughtError) {1877 hasUncaughtError = true;1878 firstUncaughtError = error;1879 }1880}1881export const onUncaughtError = prepareToThrowUncaughtError;1882function captureCommitPhaseErrorOnRoot(1883 rootFiber: Fiber,1884 sourceFiber: Fiber,1885 error: mixed,1886) {1887 const errorInfo = createCapturedValue(error, sourceFiber);1888 const update = createRootErrorUpdate(rootFiber, errorInfo, (SyncLane: Lane));1889 enqueueUpdate(rootFiber, update, (SyncLane: Lane));1890 const eventTime = requestEventTime();1891 const root = markUpdateLaneFromFiberToRoot(rootFiber, (SyncLane: Lane));1892 if (root !== null) {1893 markRootUpdated(root, SyncLane, eventTime);1894 ensureRootIsScheduled(root, eventTime);1895 }1896}1897export function captureCommitPhaseError(1898 sourceFiber: Fiber,1899 nearestMountedAncestor: Fiber | null,1900 error: mixed,1901) {1902 if (sourceFiber.tag === HostRoot) {1903 // Error was thrown at the root. There is no parent, so the root1904 // itself should capture it.1905 captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);1906 return;1907 }1908 let fiber = null;1909 if (skipUnmountedBoundaries) {1910 fiber = nearestMountedAncestor;1911 } else {1912 fiber = sourceFiber.return;1913 }1914 while (fiber !== null) {1915 if (fiber.tag === HostRoot) {1916 captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);1917 return;1918 } else if (fiber.tag === ClassComponent) {1919 const ctor = fiber.type;1920 const instance = fiber.stateNode;1921 if (1922 typeof ctor.getDerivedStateFromError === 'function' ||1923 (typeof instance.componentDidCatch === 'function' &&1924 !isAlreadyFailedLegacyErrorBoundary(instance))1925 ) {1926 const errorInfo = createCapturedValue(error, sourceFiber);1927 const update = createClassErrorUpdate(1928 fiber,1929 errorInfo,1930 (SyncLane: Lane),1931 );1932 enqueueUpdate(fiber, update, (SyncLane: Lane));1933 const eventTime = requestEventTime();1934 const root = markUpdateLaneFromFiberToRoot(fiber, (SyncLane: Lane));1935 if (root !== null) {1936 markRootUpdated(root, SyncLane, eventTime);1937 ensureRootIsScheduled(root, eventTime);1938 }1939 return;1940 }1941 }1942 fiber = fiber.return;1943 }1944 if (__DEV__) {1945 // TODO: Until we re-land skipUnmountedBoundaries (see #20147), this warning1946 // will fire for errors that are thrown by destroy functions inside deleted1947 // trees. What it should instead do is propagate the error to the parent of1948 // the deleted tree. In the meantime, do not add this warning to the1949 // allowlist; this is only for our internal use.1950 console.error(1951 'Internal React error: Attempted to capture a commit phase error ' +1952 'inside a detached tree. This indicates a bug in React. Likely ' +1953 'causes include deleting the same fiber more than once, committing an ' +1954 'already-finished tree, or an inconsistent return pointer.\n\n' +1955 'Error message:\n\n%s',1956 error,1957 );1958 }1959}1960export function pingSuspendedRoot(1961 root: FiberRoot,1962 wakeable: Wakeable,1963 pingedLanes: Lanes,1964) {1965 const pingCache = root.pingCache;1966 if (pingCache !== null) {1967 // The wakeable resolved, so we no longer need to memoize, because it will1968 // never be thrown again.1969 pingCache.delete(wakeable);1970 }1971 const eventTime = requestEventTime();1972 markRootPinged(root, pingedLanes, eventTime);1973 if (1974 workInProgressRoot === root &&1975 isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)1976 ) {1977 // Received a ping at the same priority level at which we're currently1978 // rendering. We might want to restart this render. This should mirror1979 // the logic of whether or not a root suspends once it completes.1980 // TODO: If we're rendering sync either due to Sync, Batched or expired,1981 // we should probably never restart.1982 // If we're suspended with delay, or if it's a retry, we'll always suspend1983 // so we can always restart.1984 if (1985 workInProgressRootExitStatus === RootSuspendedWithDelay ||1986 (workInProgressRootExitStatus === RootSuspended &&1987 includesOnlyRetries(workInProgressRootRenderLanes) &&1988 now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS)1989 ) {1990 // Restart from the root.1991 prepareFreshStack(root, NoLanes);1992 } else {1993 // Even though we can't restart right now, we might get an1994 // opportunity later. So we mark this render as having a ping.1995 workInProgressRootPingedLanes = mergeLanes(1996 workInProgressRootPingedLanes,1997 pingedLanes,1998 );1999 }2000 }2001 ensureRootIsScheduled(root, eventTime);2002}2003function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {2004 // The boundary fiber (a Suspense component or SuspenseList component)2005 // previously was rendered in its fallback state. One of the promises that2006 // suspended it has resolved, which means at least part of the tree was2007 // likely unblocked. Try rendering again, at a new lanes.2008 if (retryLane === NoLane) {2009 // TODO: Assign this to `suspenseState.retryLane`? to avoid2010 // unnecessary entanglement?2011 retryLane = requestRetryLane(boundaryFiber);2012 }2013 // TODO: Special case idle priority?2014 const eventTime = requestEventTime();2015 const root = markUpdateLaneFromFiberToRoot(boundaryFiber, retryLane);2016 if (root !== null) {2017 markRootUpdated(root, retryLane, eventTime);2018 ensureRootIsScheduled(root, eventTime);2019 }2020}2021export function retryDehydratedSuspenseBoundary(boundaryFiber: Fiber) {2022 const suspenseState: null | SuspenseState = boundaryFiber.memoizedState;2023 let retryLane = NoLane;2024 if (suspenseState !== null) {2025 retryLane = suspenseState.retryLane;2026 }2027 retryTimedOutBoundary(boundaryFiber, retryLane);2028}2029export function resolveRetryWakeable(boundaryFiber: Fiber, wakeable: Wakeable) {2030 let retryLane = NoLane; // Default2031 let retryCache: WeakSet<Wakeable> | Set<Wakeable> | null;2032 if (enableSuspenseServerRenderer) {2033 switch (boundaryFiber.tag) {2034 case SuspenseComponent:2035 retryCache = boundaryFiber.stateNode;2036 const suspenseState: null | SuspenseState = boundaryFiber.memoizedState;2037 if (suspenseState !== null) {2038 retryLane = suspenseState.retryLane;2039 }2040 break;2041 case SuspenseListComponent:2042 retryCache = boundaryFiber.stateNode;2043 break;2044 default:2045 invariant(2046 false,2047 'Pinged unknown suspense boundary type. ' +2048 'This is probably a bug in React.',2049 );2050 }2051 } else {2052 retryCache = boundaryFiber.stateNode;2053 }2054 if (retryCache !== null) {2055 // The wakeable resolved, so we no longer need to memoize, because it will2056 // never be thrown again.2057 retryCache.delete(wakeable);2058 }2059 retryTimedOutBoundary(boundaryFiber, retryLane);2060}2061// Computes the next Just Noticeable Difference (JND) boundary.2062// The theory is that a person can't tell the difference between small differences in time.2063// Therefore, if we wait a bit longer than necessary that won't translate to a noticeable2064// difference in the experience. However, waiting for longer might mean that we can avoid2065// showing an intermediate loading state. The longer we have already waited, the harder it2066// is to tell small differences in time. Therefore, the longer we've already waited,2067// the longer we can wait additionally. At some point we have to give up though.2068// We pick a train model where the next boundary commits at a consistent schedule.2069// These particular numbers are vague estimates. We expect to adjust them based on research.2070function jnd(timeElapsed: number) {2071 return timeElapsed < 1202072 ? 1202073 : timeElapsed < 4802074 ? 4802075 : timeElapsed < 10802076 ? 10802077 : timeElapsed < 19202078 ? 19202079 : timeElapsed < 30002080 ? 30002081 : timeElapsed < 43202082 ? 43202083 : ceil(timeElapsed / 1960) * 1960;2084}2085function checkForNestedUpdates() {2086 if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {2087 nestedUpdateCount = 0;2088 rootWithNestedUpdates = null;2089 invariant(2090 false,2091 'Maximum update depth exceeded. This can happen when a component ' +2092 'repeatedly calls setState inside componentWillUpdate or ' +2093 'componentDidUpdate. React limits the number of nested updates to ' +2094 'prevent infinite loops.',2095 );2096 }2097 if (__DEV__) {2098 if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {2099 nestedPassiveUpdateCount = 0;2100 console.error(2101 'Maximum update depth exceeded. This can happen when a component ' +2102 "calls setState inside useEffect, but useEffect either doesn't " +2103 'have a dependency array, or one of the dependencies changes on ' +2104 'every render.',2105 );2106 }2107 }2108}2109function flushRenderPhaseStrictModeWarningsInDEV() {2110 if (__DEV__) {2111 ReactStrictModeWarnings.flushLegacyContextWarning();2112 if (warnAboutDeprecatedLifecycles) {2113 ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();2114 }2115 }2116}2117function commitDoubleInvokeEffectsInDEV(2118 fiber: Fiber,2119 hasPassiveEffects: boolean,2120) {2121 if (__DEV__ && enableStrictEffects) {2122 // TODO (StrictEffects) Should we set a marker on the root if it contains strict effects2123 // so we don't traverse unnecessarily? similar to subtreeFlags but just at the root level.2124 // Maybe not a big deal since this is DEV only behavior.2125 setCurrentDebugFiberInDEV(fiber);2126 invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectUnmountInDEV);2127 if (hasPassiveEffects) {2128 invokeEffectsInDev(2129 fiber,2130 MountPassiveDev,2131 invokePassiveEffectUnmountInDEV,2132 );2133 }2134 invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectMountInDEV);2135 if (hasPassiveEffects) {2136 invokeEffectsInDev(fiber, MountPassiveDev, invokePassiveEffectMountInDEV);2137 }2138 resetCurrentDebugFiberInDEV();2139 }2140}2141function invokeEffectsInDev(2142 firstChild: Fiber,2143 fiberFlags: Flags,2144 invokeEffectFn: (fiber: Fiber) => void,2145): void {2146 if (__DEV__ && enableStrictEffects) {2147 // We don't need to re-check StrictEffectsMode here.2148 // This function is only called if that check has already passed.2149 let current = firstChild;2150 let subtreeRoot = null;2151 while (current !== null) {2152 const primarySubtreeFlag = current.subtreeFlags & fiberFlags;2153 if (2154 current !== subtreeRoot &&2155 current.child !== null &&2156 primarySubtreeFlag !== NoFlags2157 ) {2158 current = current.child;2159 } else {2160 if ((current.flags & fiberFlags) !== NoFlags) {2161 invokeEffectFn(current);2162 }2163 if (current.sibling !== null) {2164 current = current.sibling;2165 } else {2166 current = subtreeRoot = current.return;2167 }2168 }2169 }2170 }2171}2172let didWarnStateUpdateForNotYetMountedComponent: Set<string> | null = null;2173function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber) {2174 if (__DEV__) {2175 if ((executionContext & RenderContext) !== NoContext) {2176 // We let the other warning about render phase updates deal with this one.2177 return;2178 }2179 if (!(fiber.mode & ConcurrentMode)) {2180 return;2181 }2182 const tag = fiber.tag;2183 if (2184 tag !== IndeterminateComponent &&2185 tag !== HostRoot &&2186 tag !== ClassComponent &&2187 tag !== FunctionComponent &&2188 tag !== ForwardRef &&2189 tag !== MemoComponent &&2190 tag !== SimpleMemoComponent2191 ) {2192 // Only warn for user-defined components, not internal ones like Suspense.2193 return;2194 }2195 // We show the whole stack but dedupe on the top component's name because2196 // the problematic code almost always lies inside that component.2197 const componentName = getComponentNameFromFiber(fiber) || 'ReactComponent';2198 if (didWarnStateUpdateForNotYetMountedComponent !== null) {2199 if (didWarnStateUpdateForNotYetMountedComponent.has(componentName)) {2200 return;2201 }2202 didWarnStateUpdateForNotYetMountedComponent.add(componentName);2203 } else {2204 didWarnStateUpdateForNotYetMountedComponent = new Set([componentName]);2205 }2206 const previousFiber = ReactCurrentFiberCurrent;2207 try {2208 setCurrentDebugFiberInDEV(fiber);2209 console.error(2210 "Can't perform a React state update on a component that hasn't mounted yet. " +2211 'This indicates that you have a side-effect in your render function that ' +2212 'asynchronously later calls tries to update the component. Move this work to ' +2213 'useEffect instead.',2214 );2215 } finally {2216 if (previousFiber) {2217 setCurrentDebugFiberInDEV(fiber);2218 } else {2219 resetCurrentDebugFiberInDEV();2220 }2221 }2222 }2223}2224let beginWork;2225if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {2226 const dummyFiber = null;2227 beginWork = (current, unitOfWork, lanes) => {2228 // If a component throws an error, we replay it again in a synchronously2229 // dispatched event, so that the debugger will treat it as an uncaught2230 // error See ReactErrorUtils for more information.2231 // Before entering the begin phase, copy the work-in-progress onto a dummy2232 // fiber. If beginWork throws, we'll use this to reset the state.2233 const originalWorkInProgressCopy = assignFiberPropertiesInDEV(2234 dummyFiber,2235 unitOfWork,2236 );2237 try {2238 return originalBeginWork(current, unitOfWork, lanes);2239 } catch (originalError) {2240 if (2241 originalError !== null &&2242 typeof originalError === 'object' &&2243 typeof originalError.then === 'function'2244 ) {2245 // Don't replay promises. Treat everything else like an error.2246 throw originalError;2247 }2248 // Keep this code in sync with handleError; any changes here must have2249 // corresponding changes there.2250 resetContextDependencies();2251 resetHooksAfterThrow();2252 // Don't reset current debug fiber, since we're about to work on the2253 // same fiber again.2254 // Unwind the failed stack frame2255 unwindInterruptedWork(unitOfWork, workInProgressRootRenderLanes);2256 // Restore the original properties of the fiber.2257 assignFiberPropertiesInDEV(unitOfWork, originalWorkInProgressCopy);2258 if (enableProfilerTimer && unitOfWork.mode & ProfileMode) {2259 // Reset the profiler timer.2260 startProfilerTimer(unitOfWork);2261 }2262 // Run beginWork again.2263 invokeGuardedCallback(2264 null,2265 originalBeginWork,...
Using AI Code Generation
1const { resetHooksAfterThrow } = require('@playwright/test/lib/test');2const { test } = require('@playwright/test');3test('test', async ({ page }) => {4 resetHooksAfterThrow();5 throw new Error('test');6});7 ✘ test (1s)8 1 failed (1s)9 › test.js:8:11 at Object.<anonymous> (test.js:8:11)
Using AI Code Generation
1const { resetHooksAfterThrow } = require('playwright/lib/server/frames');2resetHooksAfterThrow();3const { resetHooksAfterThrow } = require('playwright/lib/server/frames');4resetHooksAfterThrow();5const { resetHooksAfterThrow } = require('playwright/lib/server/frames');6resetHooksAfterThrow();7const { resetHooksAfterThrow } = require('playwright/lib/server/frames');8resetHooksAfterThrow();9const { resetHooksAfterThrow } = require('playwright/lib/server/frames');10resetHooksAfterThrow();11const { resetHooksAfterThrow } = require('playwright/lib/server/frames');12resetHooksAfterThrow();13const { resetHooksAfterThrow } = require('playwright/lib/server/frames');14resetHooksAfterThrow();15const { resetHooksAfterThrow } = require('playwright/lib/server/frames');16resetHooksAfterThrow();17const { resetHooksAfterThrow } = require('playwright/lib/server/frames');18resetHooksAfterThrow();19const { resetHooksAfterThrow } = require('playwright/lib/server/frames');20resetHooksAfterThrow();21const { resetHooksAfterThrow } = require('playwright/lib/server/frames');22resetHooksAfterThrow();23const { resetHooksAfterThrow } = require('playwright/lib/server/frames');24resetHooksAfterThrow();25const { resetHooksAfterThrow } = require('playwright/lib/server/frames');26resetHooksAfterThrow();27const { resetHooksAfterThrow } = require('playwright/lib/server/frames');28resetHooksAfterThrow();
Using AI Code Generation
1const { resetHooksAfterThrow } = require('playwright/lib/server/frames');2resetHooksAfterThrow();3await page.click('button');4resetHooksAfterThrow();5await page.click('button');6resetHooksAfterThrow();7await page.click('button');8resetHooksAfterThrow();9await page.click('button');10resetHooksAfterThrow();11await page.click('button');12resetHooksAfterThrow();13await page.click('button');14resetHooksAfterThrow();15await page.click('button');16resetHooksAfterThrow();17await page.click('button');18resetHooksAfterThrow();19await page.click('button');20resetHooksAfterThrow();21await page.click('button');22resetHooksAfterThrow();23await page.click('button');24resetHooksAfterThrow();
Using AI Code Generation
1const { resetHooksAfterThrow } = require('playwright/lib/internal/hookScope');2const { test, expect } = require('@playwright/test');3test('test', async ({ page }) => {4 expect(true).toBe(false);5 resetHooksAfterThrow();6});
Using AI Code Generation
1const { resetHooksAfterThrow } = require('playwright/lib/server/frames');2resetHooksAfterThrow();3const { resetHooksAfterThrow } = require('playwright/lib/server/frames');4resetHooksAfterThrow();5const { resetHooksAfterThrow } = require('playwright/lib/server/frames');6resetHooksAfterThrow();7const { resetHooksAfterThrow } = require('playwright/lib/server/frames');8resetHooksAfterThrow();9const { resetHooksAfterThrow } = require('playwright/lib/server/frames');10resetHooksAfterThrow();11const { resetHooksAfterThrow } = require('playwright/lib/server/frames');12resetHooksAfterThrow();13const { resetHooksAfterThrow } = require('playwright/lib/server/frames');14resetHooksAfterThrow();15const { resetHooksAfterThrow } = require('playwright/lib/server/frames');16resetHooksAfterThrow();17const { resetHooksAfterThrow } = require('playwright/lib/server/frames');18resetHooksAfterThrow();
Using AI Code Generation
1const { resetHooksAfterThrow } = require('playwright/lib/server/frames');2resetHooksAfterThrow();3const { resetHooksAfterThrow } = require('playwright/lib/server/frames');4resetHooksAfterThrow();5const { resetHooksAfterThrow } = require('playwright/lib/server/frames');6resetHooksAfterThrow();7const { resetHooksAfterThrow } = require('playwright/lib/server/frames');8resetHooksAfterThrow();9const { resetHooksAfterThrow } = require('playwright/lib/server/frames');10resetHooksAfterThrow();11const { resetHooksAfterThrow } = require('playwright/lib/server/frames');12resetHooksAfterThrow();13const { resetHooksAfterThrow } = require('playwright/lib/server/frames');14resetHooksAfterThrow();15const { resetHooksAfterThrow } = require('playwright/lib/server/frames');16resetHooksAfterThrow();17const { resetHooksAfterThrow } = require('playwright/lib/server/frames');18resetHooksAfterThrow();
Using AI Code Generation
1const { resetHooksAfterThrow } = require('@playwright/test/lib/runner/test');2const { resetHooksAfterThrow } = require('@playwright/test/lib/runner/test');3const { resetHooksAfterThrow } = require('@playwright/test/lib/runner/test');4const { resetHooksAfterThrow } = require('@playwright/test/lib/runner/test');5const { resetHooksAfterThrow } = require('@playwright/test/lib/runner/test');6const { resetHooksAfterThrow } = require('@playwright/test/lib/runner/test');7const { resetHooksAfterThrow } = require('@playwright/test/lib/runner/test');8const { resetHooksAfterThrow } = require('@playwright/test/lib/runner/test');9const { resetHooksAfterThrow } = require('@playwright/test/lib/runner/test');10const { resetHooksAfterThrow } = require('@playwright/test/lib/runner/test');11const { resetHooksAfterThrow } = require('@playwright/test/lib/runner/test');12const { resetHooksAfterThrow } = require('@playwright/test/lib/runner/test');
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!!