How to use resetHooksAfterThrow method in Playwright Internal

Best JavaScript code snippet using playwright-internal

ReactFiberWorkLoop.js

Source:ReactFiberWorkLoop.js Github

copy

Full Screen

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

Full Screen

Full Screen

ReactFiberWorkLoop.new.js

Source:ReactFiberWorkLoop.new.js Github

copy

Full Screen

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

Full Screen

Full Screen

ReactFiberWorkLoop.old.js

Source:ReactFiberWorkLoop.old.js Github

copy

Full Screen

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

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

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)

Full Screen

Using AI Code Generation

copy

Full Screen

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

Full Screen

Using AI Code Generation

copy

Full Screen

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

Full Screen

Using AI Code Generation

copy

Full Screen

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

Full Screen

Using AI Code Generation

copy

Full Screen

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

Full Screen

Using AI Code Generation

copy

Full Screen

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

Full Screen

Using AI Code Generation

copy

Full Screen

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

Full Screen

Playwright tutorial

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

Chapters:

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

Run Playwright Internal automation tests on LambdaTest cloud grid

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

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful