Best JavaScript code snippet using playwright-internal
ReactFiberWorkLoop.js
Source:ReactFiberWorkLoop.js
...1585 // 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....
ReactFiberWorkLoop.old.js
Source:ReactFiberWorkLoop.old.js
...1170 // TODO: Might be better if `flushPassiveEffects` did not automatically1171 // flush synchronous work at the end, to avoid factoring hazards like this.1172 flushPassiveEffects();1173 } while (rootWithPendingPassiveEffects !== null);1174 flushRenderPhaseStrictModeWarningsInDEV();1175 if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) {1176 {1177 throw Error( "Should not already be working." );1178 }1179 }1180 var finishedWork = root.finishedWork;1181 var lanes = root.finishedLanes;1182 {1183 markCommitStarted(lanes);1184 }1185 if (finishedWork === null) {1186 {1187 markCommitStopped();1188 }1189 return null;1190 }1191 root.finishedWork = null;1192 root.finishedLanes = NoLanes;1193 if (!(finishedWork !== root.current)) {1194 {1195 throw Error( "Cannot commit the same tree as before. This error is likely caused by a bug in React. Please file an issue." );1196 }1197 } // commitRoot never returns a continuation; it always finishes synchronously.1198 // So we can clear these now to allow a new callback to be scheduled.1199 root.callbackNode = null; // Update the first and last pending times on this root. The new first1200 // pending time is whatever is left on the root fiber.1201 var remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);1202 markRootFinished(root, remainingLanes); // Clear already finished discrete updates in case that a later call of1203 // `flushDiscreteUpdates` starts a useless render pass which may cancels1204 // a scheduled timeout.1205 if (rootsWithPendingDiscreteUpdates !== null) {1206 if (!hasDiscreteLanes(remainingLanes) && rootsWithPendingDiscreteUpdates.has(root)) {1207 rootsWithPendingDiscreteUpdates.delete(root);1208 }1209 }1210 if (root === workInProgressRoot) {1211 // We can reset these now that they are finished.1212 workInProgressRoot = null;1213 workInProgress = null;1214 workInProgressRootRenderLanes = NoLanes;1215 } // Get the list of effects.1216 var firstEffect;1217 if (finishedWork.flags > PerformedWork) {1218 // A fiber's effect list consists only of its children, not itself. So if1219 // the root has an effect, we need to add it to the end of the list. The1220 // resulting list is the set that would belong to the root's parent, if it1221 // had one; that is, all the effects in the tree including the root.1222 if (finishedWork.lastEffect !== null) {1223 finishedWork.lastEffect.nextEffect = finishedWork;1224 firstEffect = finishedWork.firstEffect;1225 } else {1226 firstEffect = finishedWork;1227 }1228 } else {1229 // There is no effect on the root.1230 firstEffect = finishedWork.firstEffect;1231 }1232 if (firstEffect !== null) {1233 var prevExecutionContext = executionContext;1234 executionContext |= CommitContext;1235 var prevInteractions = pushInteractions(root); // Reset this to null before calling lifecycles1236 ReactCurrentOwner$2.current = null; // The commit phase is broken into several sub-phases. We do a separate pass1237 // of the effect list for each phase: all mutation effects come before all1238 // layout effects, and so on.1239 // The first phase a "before mutation" phase. We use this phase to read the1240 // state of the host tree right before we mutate it. This is where1241 // getSnapshotBeforeUpdate is called.1242 focusedInstanceHandle = prepareForCommit(root.containerInfo);1243 shouldFireAfterActiveInstanceBlur = false;1244 nextEffect = firstEffect;1245 do {1246 {1247 invokeGuardedCallback(null, commitBeforeMutationEffects, null);1248 if (hasCaughtError()) {1249 if (!(nextEffect !== null)) {1250 {1251 throw Error( "Should be working on an effect." );1252 }1253 }1254 var error = clearCaughtError();1255 captureCommitPhaseError(nextEffect, error);1256 nextEffect = nextEffect.nextEffect;1257 }1258 }1259 } while (nextEffect !== null); // We no longer need to track the active instance fiber1260 focusedInstanceHandle = null;1261 {1262 // Mark the current commit time to be shared by all Profilers in this1263 // batch. This enables them to be grouped later.1264 recordCommitTime();1265 } // The next phase is the mutation phase, where we mutate the host tree.1266 nextEffect = firstEffect;1267 do {1268 {1269 invokeGuardedCallback(null, commitMutationEffects, null, root, renderPriorityLevel);1270 if (hasCaughtError()) {1271 if (!(nextEffect !== null)) {1272 {1273 throw Error( "Should be working on an effect." );1274 }1275 }1276 var _error = clearCaughtError();1277 captureCommitPhaseError(nextEffect, _error);1278 nextEffect = nextEffect.nextEffect;1279 }1280 }1281 } while (nextEffect !== null);1282 resetAfterCommit(root.containerInfo); // The work-in-progress tree is now the current tree. This must come after1283 // the mutation phase, so that the previous tree is still current during1284 // componentWillUnmount, but before the layout phase, so that the finished1285 // work is current during componentDidMount/Update.1286 root.current = finishedWork; // The next phase is the layout phase, where we call effects that read1287 // the host tree after it's been mutated. The idiomatic use case for this is1288 // layout, but class component lifecycles also fire here for legacy reasons.1289 nextEffect = firstEffect;1290 do {1291 {1292 invokeGuardedCallback(null, commitLayoutEffects, null, root, lanes);1293 if (hasCaughtError()) {1294 if (!(nextEffect !== null)) {1295 {1296 throw Error( "Should be working on an effect." );1297 }1298 }1299 var _error2 = clearCaughtError();1300 captureCommitPhaseError(nextEffect, _error2);1301 nextEffect = nextEffect.nextEffect;1302 }1303 }1304 } while (nextEffect !== null);1305 nextEffect = null; // Tell Scheduler to yield at the end of the frame, so the browser has an1306 // opportunity to paint.1307 requestPaint();1308 {1309 popInteractions(prevInteractions);1310 }1311 executionContext = prevExecutionContext;1312 } else {1313 // No effects.1314 root.current = finishedWork; // Measure these anyway so the flamegraph explicitly shows that there were1315 // no effects.1316 // TODO: Maybe there's a better way to report this.1317 {1318 recordCommitTime();1319 }1320 }1321 var rootDidHavePassiveEffects = rootDoesHavePassiveEffects;1322 if (rootDoesHavePassiveEffects) {1323 // This commit has passive effects. Stash a reference to them. But don't1324 // schedule a callback until after flushing layout work.1325 rootDoesHavePassiveEffects = false;1326 rootWithPendingPassiveEffects = root;1327 pendingPassiveEffectsLanes = lanes;1328 pendingPassiveEffectsRenderPriority = renderPriorityLevel;1329 } else {1330 // We are done with the effect chain at this point so let's clear the1331 // nextEffect pointers to assist with GC. If we have passive effects, we'll1332 // clear this in flushPassiveEffects.1333 nextEffect = firstEffect;1334 while (nextEffect !== null) {1335 var nextNextEffect = nextEffect.nextEffect;1336 nextEffect.nextEffect = null;1337 if (nextEffect.flags & Deletion) {1338 detachFiberAfterEffects(nextEffect);1339 }1340 nextEffect = nextNextEffect;1341 }1342 } // Read this again, since an effect might have updated it1343 remainingLanes = root.pendingLanes; // Check if there's remaining work on this root1344 if (remainingLanes !== NoLanes) {1345 {1346 if (spawnedWorkDuringRender !== null) {1347 var expirationTimes = spawnedWorkDuringRender;1348 spawnedWorkDuringRender = null;1349 for (var i = 0; i < expirationTimes.length; i++) {1350 scheduleInteractions(root, expirationTimes[i], root.memoizedInteractions);1351 }1352 }1353 schedulePendingInteractions(root, remainingLanes);1354 }1355 } else {1356 // If there's no remaining work, we can clear the set of already failed1357 // error boundaries.1358 legacyErrorBoundariesThatAlreadyFailed = null;1359 }1360 {1361 if (!rootDidHavePassiveEffects) {1362 // If there are no passive effects, then we can complete the pending interactions.1363 // Otherwise, we'll wait until after the passive effects are flushed.1364 // Wait to do this until after remaining work has been scheduled,1365 // so that we don't prematurely signal complete for interactions when there's e.g. hidden work.1366 finishPendingInteractions(root, lanes);1367 }1368 }1369 if (remainingLanes === SyncLane) {1370 // Count the number of times the root synchronously re-renders without1371 // finishing. If there are too many, it indicates an infinite update loop.1372 if (root === rootWithNestedUpdates) {1373 nestedUpdateCount++;1374 } else {1375 nestedUpdateCount = 0;1376 rootWithNestedUpdates = root;1377 }1378 } else {1379 nestedUpdateCount = 0;1380 }1381 onCommitRoot(finishedWork.stateNode, renderPriorityLevel);1382 {1383 onCommitRoot$1();1384 } // Always call this before exiting `commitRoot`, to ensure that any1385 // additional work on this root is scheduled.1386 ensureRootIsScheduled(root, now());1387 if (hasUncaughtError) {1388 hasUncaughtError = false;1389 var _error3 = firstUncaughtError;1390 firstUncaughtError = null;1391 throw _error3;1392 }1393 if ((executionContext & LegacyUnbatchedContext) !== NoContext) {1394 {1395 markCommitStopped();1396 } // This is a legacy edge case. We just committed the initial mount of1397 // a ReactDOM.render-ed root inside of batchedUpdates. The commit fired1398 // synchronously, but layout updates should be deferred until the end1399 // of the batch.1400 return null;1401 } // If layout work was scheduled, flush it now.1402 flushSyncCallbackQueue();1403 {1404 markCommitStopped();1405 }1406 return null;1407 }1408 function commitBeforeMutationEffects() {1409 while (nextEffect !== null) {1410 var current = nextEffect.alternate;1411 if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {1412 if ((nextEffect.flags & Deletion) !== NoFlags) {1413 if (doesFiberContain(nextEffect, focusedInstanceHandle)) {1414 shouldFireAfterActiveInstanceBlur = true;1415 }1416 } else {1417 // TODO: Move this out of the hot path using a dedicated effect tag.1418 if (nextEffect.tag === SuspenseComponent && isSuspenseBoundaryBeingHidden(current, nextEffect) && doesFiberContain(nextEffect, focusedInstanceHandle)) {1419 shouldFireAfterActiveInstanceBlur = true;1420 }1421 }1422 }1423 var flags = nextEffect.flags;1424 if ((flags & Snapshot) !== NoFlags) {1425 setCurrentFiber(nextEffect);1426 commitBeforeMutationLifeCycles(current, nextEffect);1427 resetCurrentFiber();1428 }1429 if ((flags & Passive) !== NoFlags) {1430 // If there are passive effects, schedule a callback to flush at1431 // the earliest opportunity.1432 if (!rootDoesHavePassiveEffects) {1433 rootDoesHavePassiveEffects = true;1434 scheduleCallback(NormalPriority$1, function () {1435 flushPassiveEffects();1436 return null;1437 });1438 }1439 }1440 nextEffect = nextEffect.nextEffect;1441 }1442 }1443 function commitMutationEffects(root, renderPriorityLevel) {1444 // TODO: Should probably move the bulk of this function to commitWork.1445 while (nextEffect !== null) {1446 setCurrentFiber(nextEffect);1447 var flags = nextEffect.flags;1448 if (flags & ContentReset) {1449 commitResetTextContent(nextEffect);1450 }1451 if (flags & Ref) {1452 var current = nextEffect.alternate;1453 if (current !== null) {1454 commitDetachRef(current);1455 }1456 } // The following switch statement is only concerned about placement,1457 // updates, and deletions. To avoid needing to add a case for every possible1458 // bitmap value, we remove the secondary effects from the effect tag and1459 // switch on that value.1460 var primaryFlags = flags & (Placement | Update | Deletion | Hydrating);1461 switch (primaryFlags) {1462 case Placement:1463 {1464 commitPlacement(nextEffect); // Clear the "placement" from effect tag so that we know that this is1465 // inserted, before any life-cycles like componentDidMount gets called.1466 // TODO: findDOMNode doesn't rely on this any more but isMounted does1467 // and isMounted is deprecated anyway so we should be able to kill this.1468 nextEffect.flags &= ~Placement;1469 break;1470 }1471 case PlacementAndUpdate:1472 {1473 // Placement1474 commitPlacement(nextEffect); // Clear the "placement" from effect tag so that we know that this is1475 // inserted, before any life-cycles like componentDidMount gets called.1476 nextEffect.flags &= ~Placement; // Update1477 var _current = nextEffect.alternate;1478 commitWork(_current, nextEffect);1479 break;1480 }1481 case Hydrating:1482 {1483 nextEffect.flags &= ~Hydrating;1484 break;1485 }1486 case HydratingAndUpdate:1487 {1488 nextEffect.flags &= ~Hydrating; // Update1489 var _current2 = nextEffect.alternate;1490 commitWork(_current2, nextEffect);1491 break;1492 }1493 case Update:1494 {1495 var _current3 = nextEffect.alternate;1496 commitWork(_current3, nextEffect);1497 break;1498 }1499 case Deletion:1500 {1501 commitDeletion(root, nextEffect);1502 break;1503 }1504 }1505 resetCurrentFiber();1506 nextEffect = nextEffect.nextEffect;1507 }1508 }1509 function commitLayoutEffects(root, committedLanes) {1510 {1511 markLayoutEffectsStarted(committedLanes);1512 } // TODO: Should probably move the bulk of this function to commitWork.1513 while (nextEffect !== null) {1514 setCurrentFiber(nextEffect);1515 var flags = nextEffect.flags;1516 if (flags & (Update | Callback)) {1517 var current = nextEffect.alternate;1518 commitLifeCycles(root, current, nextEffect);1519 }1520 {1521 if (flags & Ref) {1522 commitAttachRef(nextEffect);1523 }1524 }1525 resetCurrentFiber();1526 nextEffect = nextEffect.nextEffect;1527 }1528 {1529 markLayoutEffectsStopped();1530 }1531 }1532 function flushPassiveEffects() {1533 // Returns whether passive effects were flushed.1534 if (pendingPassiveEffectsRenderPriority !== NoPriority$1) {1535 var priorityLevel = pendingPassiveEffectsRenderPriority > NormalPriority$1 ? NormalPriority$1 : pendingPassiveEffectsRenderPriority;1536 pendingPassiveEffectsRenderPriority = NoPriority$1;1537 {1538 return runWithPriority$1(priorityLevel, flushPassiveEffectsImpl);1539 }1540 }1541 return false;1542 }1543 function enqueuePendingPassiveHookEffectMount(fiber, effect) {1544 pendingPassiveHookEffectsMount.push(effect, fiber);1545 if (!rootDoesHavePassiveEffects) {1546 rootDoesHavePassiveEffects = true;1547 scheduleCallback(NormalPriority$1, function () {1548 flushPassiveEffects();1549 return null;1550 });1551 }1552 }1553 function enqueuePendingPassiveHookEffectUnmount(fiber, effect) {1554 pendingPassiveHookEffectsUnmount.push(effect, fiber);1555 {1556 fiber.flags |= PassiveUnmountPendingDev;1557 var alternate = fiber.alternate;1558 if (alternate !== null) {1559 alternate.flags |= PassiveUnmountPendingDev;1560 }1561 }1562 if (!rootDoesHavePassiveEffects) {1563 rootDoesHavePassiveEffects = true;1564 scheduleCallback(NormalPriority$1, function () {1565 flushPassiveEffects();1566 return null;1567 });1568 }1569 }1570 function invokePassiveEffectCreate(effect) {1571 var create = effect.create;1572 effect.destroy = create();1573 }1574 function flushPassiveEffectsImpl() {1575 if (rootWithPendingPassiveEffects === null) {1576 return false;1577 }1578 var root = rootWithPendingPassiveEffects;1579 var lanes = pendingPassiveEffectsLanes;1580 rootWithPendingPassiveEffects = null;1581 pendingPassiveEffectsLanes = NoLanes;1582 if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) {1583 {1584 throw Error( "Cannot flush passive effects while already rendering." );1585 }1586 }1587 {1588 markPassiveEffectsStarted(lanes);1589 }1590 {1591 isFlushingPassiveEffects = true;1592 }1593 var prevExecutionContext = executionContext;1594 executionContext |= CommitContext;1595 var prevInteractions = pushInteractions(root); // It's important that ALL pending passive effect destroy functions are called1596 // before ANY passive effect create functions are called.1597 // Otherwise effects in sibling components might interfere with each other.1598 // e.g. a destroy function in one component may unintentionally override a ref1599 // value set by a create function in another component.1600 // Layout effects have the same constraint.1601 // First pass: Destroy stale passive effects.1602 var unmountEffects = pendingPassiveHookEffectsUnmount;1603 pendingPassiveHookEffectsUnmount = [];1604 for (var i = 0; i < unmountEffects.length; i += 2) {1605 var _effect = unmountEffects[i];1606 var fiber = unmountEffects[i + 1];1607 var destroy = _effect.destroy;1608 _effect.destroy = undefined;1609 {1610 fiber.flags &= ~PassiveUnmountPendingDev;1611 var alternate = fiber.alternate;1612 if (alternate !== null) {1613 alternate.flags &= ~PassiveUnmountPendingDev;1614 }1615 }1616 if (typeof destroy === 'function') {1617 {1618 setCurrentFiber(fiber);1619 {1620 invokeGuardedCallback(null, destroy, null);1621 }1622 if (hasCaughtError()) {1623 if (!(fiber !== null)) {1624 {1625 throw Error( "Should be working on an effect." );1626 }1627 }1628 var error = clearCaughtError();1629 captureCommitPhaseError(fiber, error);1630 }1631 resetCurrentFiber();1632 }1633 }1634 } // Second pass: Create new passive effects.1635 var mountEffects = pendingPassiveHookEffectsMount;1636 pendingPassiveHookEffectsMount = [];1637 for (var _i = 0; _i < mountEffects.length; _i += 2) {1638 var _effect2 = mountEffects[_i];1639 var _fiber = mountEffects[_i + 1];1640 {1641 setCurrentFiber(_fiber);1642 {1643 invokeGuardedCallback(null, invokePassiveEffectCreate, null, _effect2);1644 }1645 if (hasCaughtError()) {1646 if (!(_fiber !== null)) {1647 {1648 throw Error( "Should be working on an effect." );1649 }1650 }1651 var _error4 = clearCaughtError();1652 captureCommitPhaseError(_fiber, _error4);1653 }1654 resetCurrentFiber();1655 }1656 } // Note: This currently assumes there are no passive effects on the root fiber1657 // because the root is not part of its own effect list.1658 // This could change in the future.1659 var effect = root.current.firstEffect;1660 while (effect !== null) {1661 var nextNextEffect = effect.nextEffect; // Remove nextEffect pointer to assist GC1662 effect.nextEffect = null;1663 if (effect.flags & Deletion) {1664 detachFiberAfterEffects(effect);1665 }1666 effect = nextNextEffect;1667 }1668 {1669 popInteractions(prevInteractions);1670 finishPendingInteractions(root, lanes);1671 }1672 {1673 isFlushingPassiveEffects = false;1674 }1675 {1676 markPassiveEffectsStopped();1677 }1678 executionContext = prevExecutionContext;1679 flushSyncCallbackQueue(); // If additional passive effects were scheduled, increment a counter. If this1680 // exceeds the limit, we'll fire a warning.1681 nestedPassiveUpdateCount = rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;1682 return true;1683 }1684 function isAlreadyFailedLegacyErrorBoundary(instance) {1685 return legacyErrorBoundariesThatAlreadyFailed !== null && legacyErrorBoundariesThatAlreadyFailed.has(instance);1686 }1687 function markLegacyErrorBoundaryAsFailed(instance) {1688 if (legacyErrorBoundariesThatAlreadyFailed === null) {1689 legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);1690 } else {1691 legacyErrorBoundariesThatAlreadyFailed.add(instance);1692 }1693 }1694 function prepareToThrowUncaughtError(error) {1695 if (!hasUncaughtError) {1696 hasUncaughtError = true;1697 firstUncaughtError = error;1698 }1699 }1700 var onUncaughtError = prepareToThrowUncaughtError;1701 function captureCommitPhaseErrorOnRoot(rootFiber, sourceFiber, error) {1702 var errorInfo = createCapturedValue(error, sourceFiber);1703 var update = createRootErrorUpdate(rootFiber, errorInfo, SyncLane);1704 enqueueUpdate(rootFiber, update);1705 var eventTime = requestEventTime();1706 var root = markUpdateLaneFromFiberToRoot(rootFiber, SyncLane);1707 if (root !== null) {1708 markRootUpdated(root, SyncLane, eventTime);1709 ensureRootIsScheduled(root, eventTime);1710 schedulePendingInteractions(root, SyncLane);1711 }1712 }1713 function captureCommitPhaseError(sourceFiber, error) {1714 if (sourceFiber.tag === HostRoot) {1715 // Error was thrown at the root. There is no parent, so the root1716 // itself should capture it.1717 captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);1718 return;1719 }1720 var fiber = sourceFiber.return;1721 while (fiber !== null) {1722 if (fiber.tag === HostRoot) {1723 captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);1724 return;1725 } else if (fiber.tag === ClassComponent) {1726 var ctor = fiber.type;1727 var instance = fiber.stateNode;1728 if (typeof ctor.getDerivedStateFromError === 'function' || typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance)) {1729 var errorInfo = createCapturedValue(error, sourceFiber);1730 var update = createClassErrorUpdate(fiber, errorInfo, SyncLane);1731 enqueueUpdate(fiber, update);1732 var eventTime = requestEventTime();1733 var root = markUpdateLaneFromFiberToRoot(fiber, SyncLane);1734 if (root !== null) {1735 markRootUpdated(root, SyncLane, eventTime);1736 ensureRootIsScheduled(root, eventTime);1737 schedulePendingInteractions(root, SyncLane);1738 } else {1739 // This component has already been unmounted.1740 // We can't schedule any follow up work for the root because the fiber is already unmounted,1741 // but we can still call the log-only boundary so the error isn't swallowed.1742 //1743 // TODO This is only a temporary bandaid for the old reconciler fork.1744 // We can delete this special case once the new fork is merged.1745 if (typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance)) {1746 try {1747 instance.componentDidCatch(error, errorInfo);1748 } catch (errorToIgnore) {// TODO Ignore this error? Rethrow it?1749 // This is kind of an edge case.1750 }1751 }1752 }1753 return;1754 }1755 }1756 fiber = fiber.return;1757 }1758 }1759 function pingSuspendedRoot(root, wakeable, pingedLanes) {1760 var pingCache = root.pingCache;1761 if (pingCache !== null) {1762 // The wakeable resolved, so we no longer need to memoize, because it will1763 // never be thrown again.1764 pingCache.delete(wakeable);1765 }1766 var eventTime = requestEventTime();1767 markRootPinged(root, pingedLanes);1768 if (workInProgressRoot === root && isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)) {1769 // Received a ping at the same priority level at which we're currently1770 // rendering. We might want to restart this render. This should mirror1771 // the logic of whether or not a root suspends once it completes.1772 // TODO: If we're rendering sync either due to Sync, Batched or expired,1773 // we should probably never restart.1774 // If we're suspended with delay, or if it's a retry, we'll always suspend1775 // so we can always restart.1776 if (workInProgressRootExitStatus === RootSuspendedWithDelay || workInProgressRootExitStatus === RootSuspended && includesOnlyRetries(workInProgressRootRenderLanes) && now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS) {1777 // Restart from the root.1778 prepareFreshStack(root, NoLanes);1779 } else {1780 // Even though we can't restart right now, we might get an1781 // opportunity later. So we mark this render as having a ping.1782 workInProgressRootPingedLanes = mergeLanes(workInProgressRootPingedLanes, pingedLanes);1783 }1784 }1785 ensureRootIsScheduled(root, eventTime);1786 schedulePendingInteractions(root, pingedLanes);1787 }1788 function retryTimedOutBoundary(boundaryFiber, retryLane) {1789 // The boundary fiber (a Suspense component or SuspenseList component)1790 // previously was rendered in its fallback state. One of the promises that1791 // suspended it has resolved, which means at least part of the tree was1792 // likely unblocked. Try rendering again, at a new expiration time.1793 if (retryLane === NoLane) {1794 retryLane = requestRetryLane(boundaryFiber);1795 } // TODO: Special case idle priority?1796 var eventTime = requestEventTime();1797 var root = markUpdateLaneFromFiberToRoot(boundaryFiber, retryLane);1798 if (root !== null) {1799 markRootUpdated(root, retryLane, eventTime);1800 ensureRootIsScheduled(root, eventTime);1801 schedulePendingInteractions(root, retryLane);1802 }1803 }1804 function retryDehydratedSuspenseBoundary(boundaryFiber) {1805 var suspenseState = boundaryFiber.memoizedState;1806 var retryLane = NoLane;1807 if (suspenseState !== null) {1808 retryLane = suspenseState.retryLane;1809 }1810 retryTimedOutBoundary(boundaryFiber, retryLane);1811 }1812 function resolveRetryWakeable(boundaryFiber, wakeable) {1813 var retryLane = NoLane; // Default1814 var retryCache;1815 {1816 switch (boundaryFiber.tag) {1817 case SuspenseComponent:1818 retryCache = boundaryFiber.stateNode;1819 var suspenseState = boundaryFiber.memoizedState;1820 if (suspenseState !== null) {1821 retryLane = suspenseState.retryLane;1822 }1823 break;1824 case SuspenseListComponent:1825 retryCache = boundaryFiber.stateNode;1826 break;1827 default:1828 {1829 {1830 throw Error( "Pinged unknown suspense boundary type. This is probably a bug in React." );1831 }1832 }1833 }1834 }1835 if (retryCache !== null) {1836 // The wakeable resolved, so we no longer need to memoize, because it will1837 // never be thrown again.1838 retryCache.delete(wakeable);1839 }1840 retryTimedOutBoundary(boundaryFiber, retryLane);1841 } // Computes the next Just Noticeable Difference (JND) boundary.1842 // The theory is that a person can't tell the difference between small differences in time.1843 // Therefore, if we wait a bit longer than necessary that won't translate to a noticeable1844 // difference in the experience. However, waiting for longer might mean that we can avoid1845 // showing an intermediate loading state. The longer we have already waited, the harder it1846 // is to tell small differences in time. Therefore, the longer we've already waited,1847 // the longer we can wait additionally. At some point we have to give up though.1848 // We pick a train model where the next boundary commits at a consistent schedule.1849 // These particular numbers are vague estimates. We expect to adjust them based on research.1850 function jnd(timeElapsed) {1851 return timeElapsed < 120 ? 120 : timeElapsed < 480 ? 480 : timeElapsed < 1080 ? 1080 : timeElapsed < 1920 ? 1920 : timeElapsed < 3000 ? 3000 : timeElapsed < 4320 ? 4320 : ceil(timeElapsed / 1960) * 1960;1852 }1853 function checkForNestedUpdates() {1854 if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {1855 nestedUpdateCount = 0;1856 rootWithNestedUpdates = null;1857 {1858 {1859 throw Error( "Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops." );1860 }1861 }1862 }1863 {1864 if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {1865 nestedPassiveUpdateCount = 0;1866 error('Maximum update depth exceeded. This can happen when a component ' + "calls setState inside useEffect, but useEffect either doesn't " + 'have a dependency array, or one of the dependencies changes on ' + 'every render.');1867 }1868 }1869 }1870 function flushRenderPhaseStrictModeWarningsInDEV() {1871 {1872 ReactStrictModeWarnings.flushLegacyContextWarning();1873 {1874 ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();1875 }1876 }1877 }1878 var didWarnStateUpdateForNotYetMountedComponent = null;1879 function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber) {1880 {1881 if ((executionContext & RenderContext) !== NoContext) {1882 // We let the other warning about render phase updates deal with this one.1883 return;1884 }...
ReactFiberScheduler.js
Source:ReactFiberScheduler.js
...1112 return null;1113}1114function commitRootImpl(root, expirationTime) {1115 flushPassiveEffects();1116 flushRenderPhaseStrictModeWarningsInDEV();1117 invariant(1118 workPhase !== RenderPhase && workPhase !== CommitPhase,1119 'Should not already be working.',1120 );1121 const finishedWork = root.current.alternate;1122 invariant(finishedWork !== null, 'Should have a work-in-progress root.');1123 // commitRoot never returns a continuation; it always finishes synchronously.1124 // So we can clear these now to allow a new callback to be scheduled.1125 root.callbackNode = null;1126 root.callbackExpirationTime = NoWork;1127 startCommitTimer();1128 // Update the first and last pending times on this root. The new first1129 // pending time is whatever is left on the root fiber.1130 const updateExpirationTimeBeforeCommit = finishedWork.expirationTime;1131 const childExpirationTimeBeforeCommit = finishedWork.childExpirationTime;1132 const firstPendingTimeBeforeCommit =1133 childExpirationTimeBeforeCommit > updateExpirationTimeBeforeCommit1134 ? childExpirationTimeBeforeCommit1135 : updateExpirationTimeBeforeCommit;1136 root.firstPendingTime = firstPendingTimeBeforeCommit;1137 if (firstPendingTimeBeforeCommit < root.lastPendingTime) {1138 // This usually means we've finished all the work, but it can also happen1139 // when something gets downprioritized during render, like a hidden tree.1140 root.lastPendingTime = firstPendingTimeBeforeCommit;1141 }1142 if (root === workInProgressRoot) {1143 // We can reset these now that they are finished.1144 workInProgressRoot = null;1145 workInProgress = null;1146 renderExpirationTime = NoWork;1147 } else {1148 // This indicates that the last root we worked on is not the same one that1149 // we're committing now. This most commonly happens when a suspended root1150 // times out.1151 }1152 // Get the list of effects.1153 let firstEffect;1154 if (finishedWork.effectTag > PerformedWork) {1155 // A fiber's effect list consists only of its children, not itself. So if1156 // the root has an effect, we need to add it to the end of the list. The1157 // resulting list is the set that would belong to the root's parent, if it1158 // had one; that is, all the effects in the tree including the root.1159 if (finishedWork.lastEffect !== null) {1160 finishedWork.lastEffect.nextEffect = finishedWork;1161 firstEffect = finishedWork.firstEffect;1162 } else {1163 firstEffect = finishedWork;1164 }1165 } else {1166 // There is no effect on the root.1167 firstEffect = finishedWork.firstEffect;1168 }1169 if (firstEffect !== null) {1170 const prevWorkPhase = workPhase;1171 workPhase = CommitPhase;1172 let prevInteractions: Set<Interaction> | null = null;1173 if (enableSchedulerTracing) {1174 prevInteractions = __interactionsRef.current;1175 __interactionsRef.current = root.memoizedInteractions;1176 }1177 // Reset this to null before calling lifecycles1178 ReactCurrentOwner.current = null;1179 // The commit phase is broken into several sub-phases. We do a separate pass1180 // of the effect list for each phase: all mutation effects come before all1181 // layout effects, and so on.1182 // The first phase a "before mutation" phase. We use this phase to read the1183 // state of the host tree right before we mutate it. This is where1184 // getSnapshotBeforeUpdate is called.1185 startCommitSnapshotEffectsTimer();1186 prepareForCommit(root.containerInfo);1187 nextEffect = firstEffect;1188 do {1189 if (__DEV__) {1190 invokeGuardedCallback(null, commitBeforeMutationEffects, null);1191 if (hasCaughtError()) {1192 invariant(nextEffect !== null, 'Should be working on an effect.');1193 const error = clearCaughtError();1194 captureCommitPhaseError(nextEffect, error);1195 nextEffect = nextEffect.nextEffect;1196 }1197 } else {1198 try {1199 commitBeforeMutationEffects();1200 } catch (error) {1201 invariant(nextEffect !== null, 'Should be working on an effect.');1202 captureCommitPhaseError(nextEffect, error);1203 nextEffect = nextEffect.nextEffect;1204 }1205 }1206 } while (nextEffect !== null);1207 stopCommitSnapshotEffectsTimer();1208 if (enableProfilerTimer) {1209 // Mark the current commit time to be shared by all Profilers in this1210 // batch. This enables them to be grouped later.1211 recordCommitTime();1212 }1213 // The next phase is the mutation phase, where we mutate the host tree.1214 startCommitHostEffectsTimer();1215 nextEffect = firstEffect;1216 do {1217 if (__DEV__) {1218 invokeGuardedCallback(null, commitMutationEffects, null);1219 if (hasCaughtError()) {1220 invariant(nextEffect !== null, 'Should be working on an effect.');1221 const error = clearCaughtError();1222 captureCommitPhaseError(nextEffect, error);1223 nextEffect = nextEffect.nextEffect;1224 }1225 } else {1226 try {1227 commitMutationEffects();1228 } catch (error) {1229 invariant(nextEffect !== null, 'Should be working on an effect.');1230 captureCommitPhaseError(nextEffect, error);1231 nextEffect = nextEffect.nextEffect;1232 }1233 }1234 } while (nextEffect !== null);1235 stopCommitHostEffectsTimer();1236 resetAfterCommit(root.containerInfo);1237 // The work-in-progress tree is now the current tree. This must come after1238 // the mutation phase, so that the previous tree is still current during1239 // componentWillUnmount, but before the layout phase, so that the finished1240 // work is current during componentDidMount/Update.1241 root.current = finishedWork;1242 // The next phase is the layout phase, where we call effects that read1243 // the host tree after it's been mutated. The idiomatic use case for this is1244 // layout, but class component lifecycles also fire here for legacy reasons.1245 startCommitLifeCyclesTimer();1246 nextEffect = firstEffect;1247 do {1248 if (__DEV__) {1249 invokeGuardedCallback(1250 null,1251 commitLayoutEffects,1252 null,1253 root,1254 expirationTime,1255 );1256 if (hasCaughtError()) {1257 invariant(nextEffect !== null, 'Should be working on an effect.');1258 const error = clearCaughtError();1259 captureCommitPhaseError(nextEffect, error);1260 nextEffect = nextEffect.nextEffect;1261 }1262 } else {1263 try {1264 commitLayoutEffects(root, expirationTime);1265 } catch (error) {1266 invariant(nextEffect !== null, 'Should be working on an effect.');1267 captureCommitPhaseError(nextEffect, error);1268 nextEffect = nextEffect.nextEffect;1269 }1270 }1271 } while (nextEffect !== null);1272 stopCommitLifeCyclesTimer();1273 nextEffect = null;1274 if (enableSchedulerTracing) {1275 __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1276 }1277 workPhase = prevWorkPhase;1278 } else {1279 // No effects.1280 root.current = finishedWork;1281 // Measure these anyway so the flamegraph explicitly shows that there were1282 // no effects.1283 // TODO: Maybe there's a better way to report this.1284 startCommitSnapshotEffectsTimer();1285 stopCommitSnapshotEffectsTimer();1286 if (enableProfilerTimer) {1287 recordCommitTime();1288 }1289 startCommitHostEffectsTimer();1290 stopCommitHostEffectsTimer();1291 startCommitLifeCyclesTimer();1292 stopCommitLifeCyclesTimer();1293 }1294 stopCommitTimer();1295 if (rootDoesHavePassiveEffects) {1296 // This commit has passive effects. Stash a reference to them. But don't1297 // schedule a callback until after flushing layout work.1298 rootDoesHavePassiveEffects = false;1299 rootWithPendingPassiveEffects = root;1300 pendingPassiveEffectsExpirationTime = expirationTime;1301 } else {1302 if (enableSchedulerTracing) {1303 // If there are no passive effects, then we can complete the pending1304 // interactions. Otherwise, we'll wait until after the passive effects1305 // are flushed.1306 finishPendingInteractions(root, expirationTime);1307 }1308 }1309 // Check if there's remaining work on this root1310 const remainingExpirationTime = root.firstPendingTime;1311 if (remainingExpirationTime !== NoWork) {1312 const currentTime = requestCurrentTime();1313 const priorityLevel = inferPriorityFromExpirationTime(1314 currentTime,1315 remainingExpirationTime,1316 );1317 scheduleCallbackForRoot(root, priorityLevel, remainingExpirationTime);1318 } else {1319 // If there's no remaining work, we can clear the set of already failed1320 // error boundaries.1321 legacyErrorBoundariesThatAlreadyFailed = null;1322 }1323 onCommitRoot(finishedWork.stateNode);1324 if (remainingExpirationTime === Sync) {1325 // Count the number of times the root synchronously re-renders without1326 // finishing. If there are too many, it indicates an infinite update loop.1327 if (root === rootWithNestedUpdates) {1328 nestedUpdateCount++;1329 } else {1330 nestedUpdateCount = 0;1331 rootWithNestedUpdates = root;1332 }1333 } else {1334 nestedUpdateCount = 0;1335 }1336 if (hasUncaughtError) {1337 hasUncaughtError = false;1338 const error = firstUncaughtError;1339 firstUncaughtError = null;1340 throw error;1341 }1342 if (workPhase === LegacyUnbatchedPhase) {1343 // This is a legacy edge case. We just committed the initial mount of1344 // a ReactDOM.render-ed root inside of batchedUpdates. The commit fired1345 // synchronously, but layout updates should be deferred until the end1346 // of the batch.1347 return null;1348 }1349 // If layout work was scheduled, flush it now.1350 flushImmediateQueue();1351 return null;1352}1353function commitBeforeMutationEffects() {1354 while (nextEffect !== null) {1355 if ((nextEffect.effectTag & Snapshot) !== NoEffect) {1356 setCurrentDebugFiberInDEV(nextEffect);1357 recordEffect();1358 const current = nextEffect.alternate;1359 commitBeforeMutationEffectOnFiber(current, nextEffect);1360 resetCurrentDebugFiberInDEV();1361 }1362 nextEffect = nextEffect.nextEffect;1363 }1364}1365function commitMutationEffects() {1366 // TODO: Should probably move the bulk of this function to commitWork.1367 while (nextEffect !== null) {1368 setCurrentDebugFiberInDEV(nextEffect);1369 const effectTag = nextEffect.effectTag;1370 if (effectTag & ContentReset) {1371 commitResetTextContent(nextEffect);1372 }1373 if (effectTag & Ref) {1374 const current = nextEffect.alternate;1375 if (current !== null) {1376 commitDetachRef(current);1377 }1378 }1379 // The following switch statement is only concerned about placement,1380 // updates, and deletions. To avoid needing to add a case for every possible1381 // bitmap value, we remove the secondary effects from the effect tag and1382 // switch on that value.1383 let primaryEffectTag = effectTag & (Placement | Update | Deletion);1384 switch (primaryEffectTag) {1385 case Placement: {1386 commitPlacement(nextEffect);1387 // Clear the "placement" from effect tag so that we know that this is1388 // inserted, before any life-cycles like componentDidMount gets called.1389 // TODO: findDOMNode doesn't rely on this any more but isMounted does1390 // and isMounted is deprecated anyway so we should be able to kill this.1391 nextEffect.effectTag &= ~Placement;1392 break;1393 }1394 case PlacementAndUpdate: {1395 // Placement1396 commitPlacement(nextEffect);1397 // Clear the "placement" from effect tag so that we know that this is1398 // inserted, before any life-cycles like componentDidMount gets called.1399 nextEffect.effectTag &= ~Placement;1400 // Update1401 const current = nextEffect.alternate;1402 commitWork(current, nextEffect);1403 break;1404 }1405 case Update: {1406 const current = nextEffect.alternate;1407 commitWork(current, nextEffect);1408 break;1409 }1410 case Deletion: {1411 commitDeletion(nextEffect);1412 break;1413 }1414 }1415 // TODO: Only record a mutation effect if primaryEffectTag is non-zero.1416 recordEffect();1417 resetCurrentDebugFiberInDEV();1418 nextEffect = nextEffect.nextEffect;1419 }1420}1421function commitLayoutEffects(1422 root: FiberRoot,1423 committedExpirationTime: ExpirationTime,1424) {1425 // TODO: Should probably move the bulk of this function to commitWork.1426 while (nextEffect !== null) {1427 setCurrentDebugFiberInDEV(nextEffect);1428 const effectTag = nextEffect.effectTag;1429 if (effectTag & (Update | Callback)) {1430 recordEffect();1431 const current = nextEffect.alternate;1432 commitLayoutEffectOnFiber(1433 root,1434 current,1435 nextEffect,1436 committedExpirationTime,1437 );1438 }1439 if (effectTag & Ref) {1440 recordEffect();1441 commitAttachRef(nextEffect);1442 }1443 if (effectTag & Passive) {1444 rootDoesHavePassiveEffects = true;1445 }1446 resetCurrentDebugFiberInDEV();1447 nextEffect = nextEffect.nextEffect;1448 }1449}1450export function flushPassiveEffects() {1451 if (rootWithPendingPassiveEffects === null) {1452 return false;1453 }1454 const root = rootWithPendingPassiveEffects;1455 const expirationTime = pendingPassiveEffectsExpirationTime;1456 rootWithPendingPassiveEffects = null;1457 pendingPassiveEffectsExpirationTime = NoWork;1458 let prevInteractions: Set<Interaction> | null = null;1459 if (enableSchedulerTracing) {1460 prevInteractions = __interactionsRef.current;1461 __interactionsRef.current = root.memoizedInteractions;1462 }1463 invariant(1464 workPhase !== RenderPhase && workPhase !== CommitPhase,1465 'Cannot flush passive effects while already rendering.',1466 );1467 const prevWorkPhase = workPhase;1468 workPhase = CommitPhase;1469 // Note: This currently assumes there are no passive effects on the root1470 // fiber, because the root is not part of its own effect list. This could1471 // change in the future.1472 let effect = root.current.firstEffect;1473 while (effect !== null) {1474 if (__DEV__) {1475 setCurrentDebugFiberInDEV(effect);1476 invokeGuardedCallback(null, commitPassiveHookEffects, null, effect);1477 if (hasCaughtError()) {1478 invariant(effect !== null, 'Should be working on an effect.');1479 const error = clearCaughtError();1480 captureCommitPhaseError(effect, error);1481 }1482 resetCurrentDebugFiberInDEV();1483 } else {1484 try {1485 commitPassiveHookEffects(effect);1486 } catch (error) {1487 invariant(effect !== null, 'Should be working on an effect.');1488 captureCommitPhaseError(effect, error);1489 }1490 }1491 effect = effect.nextEffect;1492 }1493 if (enableSchedulerTracing) {1494 __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1495 finishPendingInteractions(root, expirationTime);1496 }1497 workPhase = prevWorkPhase;1498 flushImmediateQueue();1499 // If additional passive effects were scheduled, increment a counter. If this1500 // exceeds the limit, we'll fire a warning.1501 nestedPassiveUpdateCount =1502 rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;1503 return true;1504}1505export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {1506 return (1507 legacyErrorBoundariesThatAlreadyFailed !== null &&1508 legacyErrorBoundariesThatAlreadyFailed.has(instance)1509 );1510}1511export function markLegacyErrorBoundaryAsFailed(instance: mixed) {1512 if (legacyErrorBoundariesThatAlreadyFailed === null) {1513 legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);1514 } else {1515 legacyErrorBoundariesThatAlreadyFailed.add(instance);1516 }1517}1518function prepareToThrowUncaughtError(error: mixed) {1519 if (!hasUncaughtError) {1520 hasUncaughtError = true;1521 firstUncaughtError = error;1522 }1523}1524export const onUncaughtError = prepareToThrowUncaughtError;1525function captureCommitPhaseErrorOnRoot(1526 rootFiber: Fiber,1527 sourceFiber: Fiber,1528 error: mixed,1529) {1530 const errorInfo = createCapturedValue(error, sourceFiber);1531 const update = createRootErrorUpdate(rootFiber, errorInfo, Sync);1532 enqueueUpdate(rootFiber, update);1533 const root = markUpdateTimeFromFiberToRoot(rootFiber, Sync);1534 if (root !== null) {1535 scheduleCallbackForRoot(root, ImmediatePriority, Sync);1536 }1537}1538export function captureCommitPhaseError(sourceFiber: Fiber, error: mixed) {1539 if (sourceFiber.tag === HostRoot) {1540 // Error was thrown at the root. There is no parent, so the root1541 // itself should capture it.1542 captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);1543 return;1544 }1545 let fiber = sourceFiber.return;1546 while (fiber !== null) {1547 if (fiber.tag === HostRoot) {1548 captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);1549 return;1550 } else if (fiber.tag === ClassComponent) {1551 const ctor = fiber.type;1552 const instance = fiber.stateNode;1553 if (1554 typeof ctor.getDerivedStateFromError === 'function' ||1555 (typeof instance.componentDidCatch === 'function' &&1556 !isAlreadyFailedLegacyErrorBoundary(instance))1557 ) {1558 const errorInfo = createCapturedValue(error, sourceFiber);1559 const update = createClassErrorUpdate(1560 fiber,1561 errorInfo,1562 // TODO: This is always sync1563 Sync,1564 );1565 enqueueUpdate(fiber, update);1566 const root = markUpdateTimeFromFiberToRoot(fiber, Sync);1567 if (root !== null) {1568 scheduleCallbackForRoot(root, ImmediatePriority, Sync);1569 }1570 return;1571 }1572 }1573 fiber = fiber.return;1574 }1575}1576export function pingSuspendedRoot(1577 root: FiberRoot,1578 thenable: Thenable,1579 suspendedTime: ExpirationTime,1580) {1581 const pingCache = root.pingCache;1582 if (pingCache !== null) {1583 // The thenable resolved, so we no longer need to memoize, because it will1584 // never be thrown again.1585 pingCache.delete(thenable);1586 }1587 if (workInProgressRoot === root && renderExpirationTime === suspendedTime) {1588 // Received a ping at the same priority level at which we're currently1589 // rendering. Restart from the root. Don't need to schedule a ping because1590 // we're already working on this tree.1591 prepareFreshStack(root, renderExpirationTime);1592 return;1593 }1594 const lastPendingTime = root.lastPendingTime;1595 if (lastPendingTime < suspendedTime) {1596 // The root is no longer suspended at this time.1597 return;1598 }1599 const pingTime = root.pingTime;1600 if (pingTime !== NoWork && pingTime < suspendedTime) {1601 // There's already a lower priority ping scheduled.1602 return;1603 }1604 // Mark the time at which this ping was scheduled.1605 root.pingTime = suspendedTime;1606 const currentTime = requestCurrentTime();1607 const priorityLevel = inferPriorityFromExpirationTime(1608 currentTime,1609 suspendedTime,1610 );1611 scheduleCallbackForRoot(root, priorityLevel, suspendedTime);1612}1613export function retryTimedOutBoundary(boundaryFiber: Fiber) {1614 // The boundary fiber (a Suspense component) previously timed out and was1615 // rendered in its fallback state. One of the promises that suspended it has1616 // resolved, which means at least part of the tree was likely unblocked. Try1617 // rendering again, at a new expiration time.1618 const currentTime = requestCurrentTime();1619 const retryTime = computeExpirationForFiber(currentTime, boundaryFiber);1620 // TODO: Special case idle priority?1621 const priorityLevel = inferPriorityFromExpirationTime(currentTime, retryTime);1622 const root = markUpdateTimeFromFiberToRoot(boundaryFiber, retryTime);1623 if (root !== null) {1624 scheduleCallbackForRoot(root, priorityLevel, retryTime);1625 }1626}1627export function resolveRetryThenable(boundaryFiber: Fiber, thenable: Thenable) {1628 let retryCache: WeakSet<Thenable> | Set<Thenable> | null;1629 if (enableSuspenseServerRenderer) {1630 switch (boundaryFiber.tag) {1631 case SuspenseComponent:1632 retryCache = boundaryFiber.stateNode;1633 break;1634 case DehydratedSuspenseComponent:1635 retryCache = boundaryFiber.memoizedState;1636 break;1637 default:1638 invariant(1639 false,1640 'Pinged unknown suspense boundary type. ' +1641 'This is probably a bug in React.',1642 );1643 }1644 } else {1645 retryCache = boundaryFiber.stateNode;1646 }1647 if (retryCache !== null) {1648 // The thenable resolved, so we no longer need to memoize, because it will1649 // never be thrown again.1650 retryCache.delete(thenable);1651 }1652 retryTimedOutBoundary(boundaryFiber);1653}1654// Computes the next Just Noticeable Difference (JND) boundary.1655// The theory is that a person can't tell the difference between small differences in time.1656// Therefore, if we wait a bit longer than necessary that won't translate to a noticeable1657// difference in the experience. However, waiting for longer might mean that we can avoid1658// showing an intermediate loading state. The longer we have already waited, the harder it1659// is to tell small differences in time. Therefore, the longer we've already waited,1660// the longer we can wait additionally. At some point we have to give up though.1661// We pick a train model where the next boundary commits at a consistent schedule.1662// These particular numbers are vague estimates. We expect to adjust them based on research.1663function jnd(timeElapsed: number) {1664 return timeElapsed < 1201665 ? 1201666 : timeElapsed < 4801667 ? 4801668 : timeElapsed < 10801669 ? 10801670 : timeElapsed < 19201671 ? 19201672 : timeElapsed < 30001673 ? 30001674 : timeElapsed < 43201675 ? 43201676 : ceil(timeElapsed / 1960) * 1960;1677}1678function computeMsUntilTimeout(1679 mostRecentEventTime: ExpirationTime,1680 committedExpirationTime: ExpirationTime,1681) {1682 if (disableYielding) {1683 // Timeout immediately when yielding is disabled.1684 return 0;1685 }1686 const eventTimeMs: number = inferTimeFromExpirationTime(mostRecentEventTime);1687 const currentTimeMs: number = now();1688 const timeElapsed = currentTimeMs - eventTimeMs;1689 let msUntilTimeout = jnd(timeElapsed) - timeElapsed;1690 // Compute the time until this render pass would expire.1691 const timeUntilExpirationMs =1692 expirationTimeToMs(committedExpirationTime) - currentTimeMs;1693 // Clamp the timeout to the expiration time.1694 // TODO: Once the event time is exact instead of inferred from expiration time1695 // we don't need this.1696 if (timeUntilExpirationMs < msUntilTimeout) {1697 msUntilTimeout = timeUntilExpirationMs;1698 }1699 // This is the value that is passed to `setTimeout`.1700 return msUntilTimeout;1701}1702function checkForNestedUpdates() {1703 if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {1704 nestedUpdateCount = 0;1705 rootWithNestedUpdates = null;1706 invariant(1707 false,1708 'Maximum update depth exceeded. This can happen when a component ' +1709 'repeatedly calls setState inside componentWillUpdate or ' +1710 'componentDidUpdate. React limits the number of nested updates to ' +1711 'prevent infinite loops.',1712 );1713 }1714 if (__DEV__) {1715 if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {1716 nestedPassiveUpdateCount = 0;1717 warning(1718 false,1719 'Maximum update depth exceeded. This can happen when a component ' +1720 "calls setState inside useEffect, but useEffect either doesn't " +1721 'have a dependency array, or one of the dependencies changes on ' +1722 'every render.',1723 );1724 }1725 }1726}1727function flushRenderPhaseStrictModeWarningsInDEV() {1728 if (__DEV__) {1729 ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();1730 ReactStrictModeWarnings.flushLegacyContextWarning();1731 if (warnAboutDeprecatedLifecycles) {1732 ReactStrictModeWarnings.flushPendingDeprecationWarnings();1733 }1734 }1735}1736function stopFinishedWorkLoopTimer() {1737 const didCompleteRoot = true;1738 stopWorkLoopTimer(interruptedBy, didCompleteRoot);1739 interruptedBy = null;1740}1741function stopInterruptedWorkLoopTimer() {...
ReactFiberScheduler.new.js
Source:ReactFiberScheduler.new.js
...1086 return null;1087}1088function commitRootImpl(root, expirationTime) {1089 flushPassiveEffects();1090 flushRenderPhaseStrictModeWarningsInDEV();1091 invariant(1092 workPhase !== RenderPhase && workPhase !== CommitPhase,1093 'Should not already be working.',1094 );1095 const finishedWork = root.current.alternate;1096 invariant(finishedWork !== null, 'Should have a work-in-progress root.');1097 // commitRoot never returns a continuation; it always finishes synchronously.1098 // So we can clear these now to allow a new callback to be scheduled.1099 root.callbackNode = null;1100 root.callbackExpirationTime = NoWork;1101 startCommitTimer();1102 // Update the first and last pending times on this root. The new first1103 // pending time is whatever is left on the root fiber.1104 const updateExpirationTimeBeforeCommit = finishedWork.expirationTime;1105 const childExpirationTimeBeforeCommit = finishedWork.childExpirationTime;1106 const firstPendingTimeBeforeCommit =1107 childExpirationTimeBeforeCommit > updateExpirationTimeBeforeCommit1108 ? childExpirationTimeBeforeCommit1109 : updateExpirationTimeBeforeCommit;1110 root.firstPendingTime = firstPendingTimeBeforeCommit;1111 if (firstPendingTimeBeforeCommit < root.lastPendingTime) {1112 // This usually means we've finished all the work, but it can also happen1113 // when something gets downprioritized during render, like a hidden tree.1114 root.lastPendingTime = firstPendingTimeBeforeCommit;1115 }1116 if (root === workInProgressRoot) {1117 // We can reset these now that they are finished.1118 workInProgressRoot = null;1119 workInProgress = null;1120 renderExpirationTime = NoWork;1121 } else {1122 // This indicates that the last root we worked on is not the same one that1123 // we're committing now. This most commonly happens when a suspended root1124 // times out.1125 }1126 // Get the list of effects.1127 let firstEffect;1128 if (finishedWork.effectTag > PerformedWork) {1129 // A fiber's effect list consists only of its children, not itself. So if1130 // the root has an effect, we need to add it to the end of the list. The1131 // resulting list is the set that would belong to the root's parent, if it1132 // had one; that is, all the effects in the tree including the root.1133 if (finishedWork.lastEffect !== null) {1134 finishedWork.lastEffect.nextEffect = finishedWork;1135 firstEffect = finishedWork.firstEffect;1136 } else {1137 firstEffect = finishedWork;1138 }1139 } else {1140 // There is no effect on the root.1141 firstEffect = finishedWork.firstEffect;1142 }1143 if (firstEffect !== null) {1144 const prevWorkPhase = workPhase;1145 workPhase = CommitPhase;1146 let prevInteractions: Set<Interaction> | null = null;1147 if (enableSchedulerTracing) {1148 prevInteractions = __interactionsRef.current;1149 __interactionsRef.current = root.memoizedInteractions;1150 }1151 // Reset this to null before calling lifecycles1152 ReactCurrentOwner.current = null;1153 // The commit phase is broken into several sub-phases. We do a separate pass1154 // of the effect list for each phase: all mutation effects come before all1155 // layout effects, and so on.1156 // The first phase a "before mutation" phase. We use this phase to read the1157 // state of the host tree right before we mutate it. This is where1158 // getSnapshotBeforeUpdate is called.1159 startCommitSnapshotEffectsTimer();1160 prepareForCommit(root.containerInfo);1161 nextEffect = firstEffect;1162 do {1163 if (__DEV__) {1164 invokeGuardedCallback(null, commitBeforeMutationEffects, null);1165 if (hasCaughtError()) {1166 invariant(nextEffect !== null, 'Should be working on an effect.');1167 const error = clearCaughtError();1168 captureCommitPhaseError(nextEffect, error);1169 nextEffect = nextEffect.nextEffect;1170 }1171 } else {1172 try {1173 commitBeforeMutationEffects();1174 } catch (error) {1175 invariant(nextEffect !== null, 'Should be working on an effect.');1176 captureCommitPhaseError(nextEffect, error);1177 nextEffect = nextEffect.nextEffect;1178 }1179 }1180 } while (nextEffect !== null);1181 stopCommitSnapshotEffectsTimer();1182 if (enableProfilerTimer) {1183 // Mark the current commit time to be shared by all Profilers in this1184 // batch. This enables them to be grouped later.1185 recordCommitTime();1186 }1187 // The next phase is the mutation phase, where we mutate the host tree.1188 startCommitHostEffectsTimer();1189 nextEffect = firstEffect;1190 do {1191 if (__DEV__) {1192 invokeGuardedCallback(null, commitMutationEffects, null);1193 if (hasCaughtError()) {1194 invariant(nextEffect !== null, 'Should be working on an effect.');1195 const error = clearCaughtError();1196 captureCommitPhaseError(nextEffect, error);1197 nextEffect = nextEffect.nextEffect;1198 }1199 } else {1200 try {1201 commitMutationEffects();1202 } catch (error) {1203 invariant(nextEffect !== null, 'Should be working on an effect.');1204 captureCommitPhaseError(nextEffect, error);1205 nextEffect = nextEffect.nextEffect;1206 }1207 }1208 } while (nextEffect !== null);1209 stopCommitHostEffectsTimer();1210 resetAfterCommit(root.containerInfo);1211 // The work-in-progress tree is now the current tree. This must come after1212 // the mutation phase, so that the previous tree is still current during1213 // componentWillUnmount, but before the layout phase, so that the finished1214 // work is current during componentDidMount/Update.1215 root.current = finishedWork;1216 // The next phase is the layout phase, where we call effects that read1217 // the host tree after it's been mutated. The idiomatic use case for this is1218 // layout, but class component lifecycles also fire here for legacy reasons.1219 startCommitLifeCyclesTimer();1220 nextEffect = firstEffect;1221 do {1222 if (__DEV__) {1223 invokeGuardedCallback(1224 null,1225 commitLayoutEffects,1226 null,1227 root,1228 expirationTime,1229 );1230 if (hasCaughtError()) {1231 invariant(nextEffect !== null, 'Should be working on an effect.');1232 const error = clearCaughtError();1233 captureCommitPhaseError(nextEffect, error);1234 nextEffect = nextEffect.nextEffect;1235 }1236 } else {1237 try {1238 commitLayoutEffects(root, expirationTime);1239 } catch (error) {1240 invariant(nextEffect !== null, 'Should be working on an effect.');1241 captureCommitPhaseError(nextEffect, error);1242 nextEffect = nextEffect.nextEffect;1243 }1244 }1245 } while (nextEffect !== null);1246 stopCommitLifeCyclesTimer();1247 nextEffect = null;1248 if (enableSchedulerTracing) {1249 __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1250 }1251 workPhase = prevWorkPhase;1252 } else {1253 // No effects.1254 root.current = finishedWork;1255 // Measure these anyway so the flamegraph explicitly shows that there were1256 // no effects.1257 // TODO: Maybe there's a better way to report this.1258 startCommitSnapshotEffectsTimer();1259 stopCommitSnapshotEffectsTimer();1260 if (enableProfilerTimer) {1261 recordCommitTime();1262 }1263 startCommitHostEffectsTimer();1264 stopCommitHostEffectsTimer();1265 startCommitLifeCyclesTimer();1266 stopCommitLifeCyclesTimer();1267 }1268 stopCommitTimer();1269 if (rootDoesHavePassiveEffects) {1270 // This commit has passive effects. Stash a reference to them. But don't1271 // schedule a callback until after flushing layout work.1272 rootDoesHavePassiveEffects = false;1273 rootWithPendingPassiveEffects = root;1274 pendingPassiveEffectsExpirationTime = expirationTime;1275 } else {1276 if (enableSchedulerTracing) {1277 // If there are no passive effects, then we can complete the pending1278 // interactions. Otherwise, we'll wait until after the passive effects1279 // are flushed.1280 finishPendingInteractions(root, expirationTime);1281 }1282 }1283 // Check if there's remaining work on this root1284 const remainingExpirationTime = root.firstPendingTime;1285 if (remainingExpirationTime !== NoWork) {1286 const currentTime = requestCurrentTime();1287 const priorityLevel = inferPriorityFromExpirationTime(1288 currentTime,1289 remainingExpirationTime,1290 );1291 scheduleCallbackForRoot(root, priorityLevel, remainingExpirationTime);1292 } else {1293 // If there's no remaining work, we can clear the set of already failed1294 // error boundaries.1295 legacyErrorBoundariesThatAlreadyFailed = null;1296 }1297 onCommitRoot(finishedWork.stateNode);1298 if (remainingExpirationTime === Sync) {1299 // Count the number of times the root synchronously re-renders without1300 // finishing. If there are too many, it indicates an infinite update loop.1301 if (root === rootWithNestedUpdates) {1302 nestedUpdateCount++;1303 } else {1304 nestedUpdateCount = 0;1305 rootWithNestedUpdates = root;1306 }1307 } else {1308 nestedUpdateCount = 0;1309 }1310 if (hasUncaughtError) {1311 hasUncaughtError = false;1312 const error = firstUncaughtError;1313 firstUncaughtError = null;1314 throw error;1315 }1316 if (workPhase === LegacyUnbatchedPhase) {1317 // This is a legacy edge case. We just committed the initial mount of1318 // a ReactDOM.render-ed root inside of batchedUpdates. The commit fired1319 // synchronously, but layout updates should be deferred until the end1320 // of the batch.1321 return null;1322 }1323 // If layout work was scheduled, flush it now.1324 flushImmediateQueue();1325 return null;1326}1327function commitBeforeMutationEffects() {1328 while (nextEffect !== null) {1329 if ((nextEffect.effectTag & Snapshot) !== NoEffect) {1330 setCurrentDebugFiberInDEV(nextEffect);1331 recordEffect();1332 const current = nextEffect.alternate;1333 commitBeforeMutationEffectOnFiber(current, nextEffect);1334 resetCurrentDebugFiberInDEV();1335 }1336 nextEffect = nextEffect.nextEffect;1337 }1338}1339function commitMutationEffects() {1340 // TODO: Should probably move the bulk of this function to commitWork.1341 while (nextEffect !== null) {1342 setCurrentDebugFiberInDEV(nextEffect);1343 const effectTag = nextEffect.effectTag;1344 if (effectTag & ContentReset) {1345 commitResetTextContent(nextEffect);1346 }1347 if (effectTag & Ref) {1348 const current = nextEffect.alternate;1349 if (current !== null) {1350 commitDetachRef(current);1351 }1352 }1353 // The following switch statement is only concerned about placement,1354 // updates, and deletions. To avoid needing to add a case for every possible1355 // bitmap value, we remove the secondary effects from the effect tag and1356 // switch on that value.1357 let primaryEffectTag = effectTag & (Placement | Update | Deletion);1358 switch (primaryEffectTag) {1359 case Placement: {1360 commitPlacement(nextEffect);1361 // Clear the "placement" from effect tag so that we know that this is1362 // inserted, before any life-cycles like componentDidMount gets called.1363 // TODO: findDOMNode doesn't rely on this any more but isMounted does1364 // and isMounted is deprecated anyway so we should be able to kill this.1365 nextEffect.effectTag &= ~Placement;1366 break;1367 }1368 case PlacementAndUpdate: {1369 // Placement1370 commitPlacement(nextEffect);1371 // Clear the "placement" from effect tag so that we know that this is1372 // inserted, before any life-cycles like componentDidMount gets called.1373 nextEffect.effectTag &= ~Placement;1374 // Update1375 const current = nextEffect.alternate;1376 commitWork(current, nextEffect);1377 break;1378 }1379 case Update: {1380 const current = nextEffect.alternate;1381 commitWork(current, nextEffect);1382 break;1383 }1384 case Deletion: {1385 commitDeletion(nextEffect);1386 break;1387 }1388 }1389 // TODO: Only record a mutation effect if primaryEffectTag is non-zero.1390 recordEffect();1391 resetCurrentDebugFiberInDEV();1392 nextEffect = nextEffect.nextEffect;1393 }1394}1395function commitLayoutEffects(1396 root: FiberRoot,1397 committedExpirationTime: ExpirationTime,1398) {1399 // TODO: Should probably move the bulk of this function to commitWork.1400 while (nextEffect !== null) {1401 setCurrentDebugFiberInDEV(nextEffect);1402 const effectTag = nextEffect.effectTag;1403 if (effectTag & (Update | Callback)) {1404 recordEffect();1405 const current = nextEffect.alternate;1406 commitLayoutEffectOnFiber(1407 root,1408 current,1409 nextEffect,1410 committedExpirationTime,1411 );1412 }1413 if (effectTag & Ref) {1414 recordEffect();1415 commitAttachRef(nextEffect);1416 }1417 if (effectTag & Passive) {1418 rootDoesHavePassiveEffects = true;1419 }1420 resetCurrentDebugFiberInDEV();1421 nextEffect = nextEffect.nextEffect;1422 }1423}1424export function flushPassiveEffects() {1425 if (rootWithPendingPassiveEffects === null) {1426 return false;1427 }1428 const root = rootWithPendingPassiveEffects;1429 const expirationTime = pendingPassiveEffectsExpirationTime;1430 rootWithPendingPassiveEffects = null;1431 pendingPassiveEffectsExpirationTime = NoWork;1432 let prevInteractions: Set<Interaction> | null = null;1433 if (enableSchedulerTracing) {1434 prevInteractions = __interactionsRef.current;1435 __interactionsRef.current = root.memoizedInteractions;1436 }1437 invariant(1438 workPhase !== RenderPhase && workPhase !== CommitPhase,1439 'Cannot flush passive effects while already rendering.',1440 );1441 const prevWorkPhase = workPhase;1442 workPhase = CommitPhase;1443 // Note: This currently assumes there are no passive effects on the root1444 // fiber, because the root is not part of its own effect list. This could1445 // change in the future.1446 let effect = root.current.firstEffect;1447 while (effect !== null) {1448 if (__DEV__) {1449 setCurrentDebugFiberInDEV(effect);1450 invokeGuardedCallback(null, commitPassiveHookEffects, null, effect);1451 if (hasCaughtError()) {1452 invariant(effect !== null, 'Should be working on an effect.');1453 const error = clearCaughtError();1454 captureCommitPhaseError(effect, error);1455 }1456 resetCurrentDebugFiberInDEV();1457 } else {1458 try {1459 commitPassiveHookEffects(effect);1460 } catch (error) {1461 invariant(effect !== null, 'Should be working on an effect.');1462 captureCommitPhaseError(effect, error);1463 }1464 }1465 effect = effect.nextEffect;1466 }1467 if (enableSchedulerTracing) {1468 __interactionsRef.current = ((prevInteractions: any): Set<Interaction>);1469 finishPendingInteractions(root, expirationTime);1470 }1471 workPhase = prevWorkPhase;1472 flushImmediateQueue();1473 // If additional passive effects were scheduled, increment a counter. If this1474 // exceeds the limit, we'll fire a warning.1475 nestedPassiveUpdateCount =1476 rootWithPendingPassiveEffects === null ? 0 : nestedPassiveUpdateCount + 1;1477 return true;1478}1479export function isAlreadyFailedLegacyErrorBoundary(instance: mixed): boolean {1480 return (1481 legacyErrorBoundariesThatAlreadyFailed !== null &&1482 legacyErrorBoundariesThatAlreadyFailed.has(instance)1483 );1484}1485export function markLegacyErrorBoundaryAsFailed(instance: mixed) {1486 if (legacyErrorBoundariesThatAlreadyFailed === null) {1487 legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);1488 } else {1489 legacyErrorBoundariesThatAlreadyFailed.add(instance);1490 }1491}1492function prepareToThrowUncaughtError(error: mixed) {1493 if (!hasUncaughtError) {1494 hasUncaughtError = true;1495 firstUncaughtError = error;1496 }1497}1498export const onUncaughtError = prepareToThrowUncaughtError;1499function captureCommitPhaseErrorOnRoot(1500 rootFiber: Fiber,1501 sourceFiber: Fiber,1502 error: mixed,1503) {1504 const errorInfo = createCapturedValue(error, sourceFiber);1505 const update = createRootErrorUpdate(rootFiber, errorInfo, Sync);1506 enqueueUpdate(rootFiber, update);1507 const root = markUpdateTimeFromFiberToRoot(rootFiber, Sync);1508 if (root !== null) {1509 scheduleCallbackForRoot(root, ImmediatePriority, Sync);1510 }1511}1512export function captureCommitPhaseError(sourceFiber: Fiber, error: mixed) {1513 if (sourceFiber.tag === HostRoot) {1514 // Error was thrown at the root. There is no parent, so the root1515 // itself should capture it.1516 captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error);1517 return;1518 }1519 let fiber = sourceFiber.return;1520 while (fiber !== null) {1521 if (fiber.tag === HostRoot) {1522 captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error);1523 return;1524 } else if (fiber.tag === ClassComponent) {1525 const ctor = fiber.type;1526 const instance = fiber.stateNode;1527 if (1528 typeof ctor.getDerivedStateFromError === 'function' ||1529 (typeof instance.componentDidCatch === 'function' &&1530 !isAlreadyFailedLegacyErrorBoundary(instance))1531 ) {1532 const errorInfo = createCapturedValue(error, sourceFiber);1533 const update = createClassErrorUpdate(1534 fiber,1535 errorInfo,1536 // TODO: This is always sync1537 Sync,1538 );1539 enqueueUpdate(fiber, update);1540 const root = markUpdateTimeFromFiberToRoot(fiber, Sync);1541 if (root !== null) {1542 scheduleCallbackForRoot(root, ImmediatePriority, Sync);1543 }1544 return;1545 }1546 }1547 fiber = fiber.return;1548 }1549}1550export function pingSuspendedRoot(1551 root: FiberRoot,1552 thenable: Thenable,1553 suspendedTime: ExpirationTime,1554) {1555 const pingCache = root.pingCache;1556 if (pingCache !== null) {1557 // The thenable resolved, so we no longer need to memoize, because it will1558 // never be thrown again.1559 pingCache.delete(thenable);1560 }1561 if (workInProgressRoot === root && renderExpirationTime === suspendedTime) {1562 // Received a ping at the same priority level at which we're currently1563 // rendering. Restart from the root. Don't need to schedule a ping because1564 // we're already working on this tree.1565 prepareFreshStack(root, renderExpirationTime);1566 return;1567 }1568 const lastPendingTime = root.lastPendingTime;1569 if (lastPendingTime < suspendedTime) {1570 // The root is no longer suspended at this time.1571 return;1572 }1573 const pingTime = root.pingTime;1574 if (pingTime !== NoWork && pingTime < suspendedTime) {1575 // There's already a lower priority ping scheduled.1576 return;1577 }1578 // Mark the time at which this ping was scheduled.1579 root.pingTime = suspendedTime;1580 const currentTime = requestCurrentTime();1581 const priorityLevel = inferPriorityFromExpirationTime(1582 currentTime,1583 suspendedTime,1584 );1585 scheduleCallbackForRoot(root, priorityLevel, suspendedTime);1586}1587export function retryTimedOutBoundary(boundaryFiber: Fiber) {1588 // The boundary fiber (a Suspense component) previously timed out and was1589 // rendered in its fallback state. One of the promises that suspended it has1590 // resolved, which means at least part of the tree was likely unblocked. Try1591 // rendering again, at a new expiration time.1592 const currentTime = requestCurrentTime();1593 const retryTime = computeExpirationForFiber(currentTime, boundaryFiber);1594 // TODO: Special case idle priority?1595 const priorityLevel = inferPriorityFromExpirationTime(currentTime, retryTime);1596 const root = markUpdateTimeFromFiberToRoot(boundaryFiber, retryTime);1597 if (root !== null) {1598 scheduleCallbackForRoot(root, priorityLevel, retryTime);1599 }1600}1601export function resolveRetryThenable(boundaryFiber: Fiber, thenable: Thenable) {1602 let retryCache: WeakSet<Thenable> | Set<Thenable> | null;1603 if (enableSuspenseServerRenderer) {1604 switch (boundaryFiber.tag) {1605 case SuspenseComponent:1606 retryCache = boundaryFiber.stateNode;1607 break;1608 case DehydratedSuspenseComponent:1609 retryCache = boundaryFiber.memoizedState;1610 break;1611 default:1612 invariant(1613 false,1614 'Pinged unknown suspense boundary type. ' +1615 'This is probably a bug in React.',1616 );1617 }1618 } else {1619 retryCache = boundaryFiber.stateNode;1620 }1621 if (retryCache !== null) {1622 // The thenable resolved, so we no longer need to memoize, because it will1623 // never be thrown again.1624 retryCache.delete(thenable);1625 }1626 retryTimedOutBoundary(boundaryFiber);1627}1628export function inferStartTimeFromExpirationTime(1629 root: FiberRoot,1630 expirationTime: ExpirationTime,1631) {1632 // We don't know exactly when the update was scheduled, but we can infer an1633 // approximate start time from the expiration time.1634 const earliestExpirationTimeMs = expirationTimeToMs(root.firstPendingTime);1635 // TODO: Track this on the root instead. It's more accurate, doesn't rely on1636 // assumptions about priority, and isn't coupled to Scheduler details.1637 return earliestExpirationTimeMs - LOW_PRIORITY_EXPIRATION;1638}1639function computeMsUntilTimeout(root, absoluteTimeoutMs) {1640 if (disableYielding) {1641 // Timeout immediately when yielding is disabled.1642 return 0;1643 }1644 // Find the earliest uncommitted expiration time in the tree, including1645 // work that is suspended. The timeout threshold cannot be longer than1646 // the overall expiration.1647 const earliestExpirationTimeMs = expirationTimeToMs(root.firstPendingTime);1648 if (earliestExpirationTimeMs < absoluteTimeoutMs) {1649 absoluteTimeoutMs = earliestExpirationTimeMs;1650 }1651 // Subtract the current time from the absolute timeout to get the number1652 // of milliseconds until the timeout. In other words, convert an absolute1653 // timestamp to a relative time. This is the value that is passed1654 // to `setTimeout`.1655 let msUntilTimeout = absoluteTimeoutMs - now();1656 return msUntilTimeout < 0 ? 0 : msUntilTimeout;1657}1658function checkForNestedUpdates() {1659 if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {1660 nestedUpdateCount = 0;1661 rootWithNestedUpdates = null;1662 invariant(1663 false,1664 'Maximum update depth exceeded. This can happen when a component ' +1665 'repeatedly calls setState inside componentWillUpdate or ' +1666 'componentDidUpdate. React limits the number of nested updates to ' +1667 'prevent infinite loops.',1668 );1669 }1670 if (__DEV__) {1671 if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {1672 nestedPassiveUpdateCount = 0;1673 warning(1674 false,1675 'Maximum update depth exceeded. This can happen when a component ' +1676 "calls setState inside useEffect, but useEffect either doesn't " +1677 'have a dependency array, or one of the dependencies changes on ' +1678 'every render.',1679 );1680 }1681 }1682}1683function flushRenderPhaseStrictModeWarningsInDEV() {1684 if (__DEV__) {1685 ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();1686 ReactStrictModeWarnings.flushLegacyContextWarning();1687 if (warnAboutDeprecatedLifecycles) {1688 ReactStrictModeWarnings.flushPendingDeprecationWarnings();1689 }1690 }1691}1692function stopFinishedWorkLoopTimer() {1693 const didCompleteRoot = true;1694 stopWorkLoopTimer(interruptedBy, didCompleteRoot);1695 interruptedBy = null;1696}1697function stopInterruptedWorkLoopTimer() {...
4 - commitWork.js
Source:4 - commitWork.js
...22 // TODO: Might be better if `flushPassiveEffects` did not automatically23 // flush synchronous work at the end, to avoid factoring hazards like this.24 flushPassiveEffects();25 } while (rootWithPendingPassiveEffects !== null);26 flushRenderPhaseStrictModeWarningsInDEV();27 invariant(28 (executionContext & (RenderContext | CommitContext)) === NoContext,29 'Should not already be working.',30 );31 const finishedWork = root.finishedWork;32 const lanes = root.finishedLanes;33 if (enableSchedulingProfiler) {34 markCommitStarted(lanes);35 }36 if (finishedWork === null) {37 if (enableSchedulingProfiler) {38 markCommitStopped();39 }40 return null;...
commitRootImpl.js
Source:commitRootImpl.js
1function commitRootImpl(root, renderPriorityLevel) {2 flushPassiveEffects();3 flushRenderPhaseStrictModeWarningsInDEV();4 (function () {5 if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) {6 {7 throw ReactError(Error("Should not already be working."));8 }9 }10 })();11 var finishedWork = root.finishedWork;12 var expirationTime = root.finishedExpirationTime;13 if (finishedWork === null) {14 return null;15 }16 root.finishedWork = null;17 root.finishedExpirationTime = NoWork;...
finishSyncRender.js
Source:finishSyncRender.js
1function commitRootImpl(root, renderPriorityLevel) {2 do {3 flushPassiveEffects();4 } while (rootWithPendingPassiveEffects !== null);5 flushRenderPhaseStrictModeWarningsInDEV();6 const finishedWork = root.finishedWork;7 const expirationTime = root.finishedExpirationTime;8 if (finishedWork === null) {9 return null;10 }11 root.finishedWork = null;12 root.finishedExpirationTime = NoWork;13 root.callbackNode = null;14 root.callbackExpirationTime = NoWork;15 root.callbackPriority = NoPriority;16 root.nextKnownPendingLevel = NoWork;17 startCommitTimer();18 const remainingExpirationTimeBeforeCommit = getRemainingExpirationTime(19 finishedWork,...
ReactFabric-dev.js
Source:ReactFabric-dev.js
...58 }59+ performWork(NoWork, true);60 }61 62 function flushRenderPhaseStrictModeWarningsInDEV() {63@@ -19085,6 +19124,17 @@64 * Manipulation](docs/direct-manipulation.html)).65 */66 setNativeProps: function(nativeProps) {67+ {68+ if (warnAboutDeprecatedSetNativeProps) {69+ warningWithoutStack$1(70+ false,71+ "Warning: Calling ref.setNativeProps(nativeProps) " +72+ "is deprecated and will be removed in a future release. " +73+ "Use the setNativeProps export from the react-native package instead." +74+ "\n\timport {setNativeProps} from 'react-native';\n\tsetNativeProps(ref, nativeProps);\n"75+ );76+ }...
Using AI Code Generation
1const { flushRenderPhaseStrictModeWarningsInDEV } = require('playwright');2flushRenderPhaseStrictModeWarningsInDEV();3const { flushRenderPhaseStrictModeWarningsInDEV } = require('playwright');4flushRenderPhaseStrictModeWarningsInDEV();5const { flushRenderPhaseStrictModeWarningsInDEV } = require('playwright');6flushRenderPhaseStrictModeWarningsInDEV();7const { flushRenderPhaseStrictModeWarningsInDEV } = require('playwright');8flushRenderPhaseStrictModeWarningsInDEV();9const { flushRenderPhaseStrictModeWarningsInDEV } = require('playwright');10flushRenderPhaseStrictModeWarningsInDEV();11const { flushRenderPhaseStrictModeWarningsInDEV } = require('playwright');12flushRenderPhaseStrictModeWarningsInDEV();13const { flushRenderPhaseStrictModeWarningsInDEV } = require('playwright');14flushRenderPhaseStrictModeWarningsInDEV();15const { flushRenderPhaseStrictModeWarningsInDEV } = require('playwright');16flushRenderPhaseStrictModeWarningsInDEV();17const { flushRenderPhaseStrictModeWarningsInDEV } = require('playwright');18flushRenderPhaseStrictModeWarningsInDEV();19const { flushRenderPhaseStrictModeWarningsInDEV } = require('playwright');20flushRenderPhaseStrictModeWarningsInDEV();21const { flushRenderPhaseStrictModeWarningsInDEV } = require('playwright');22flushRenderPhaseStrictModeWarningsInDEV();23const {
Using AI Code Generation
1const playwright = require('playwright');2const { flushRenderPhaseStrictModeWarningsInDEV } = require('playwright/lib/internal');3(async () => {4 const browser = await playwright.chromium.launch();5 const context = await browser.newContext();6 const page = await context.newPage();7 const warnings = await flushRenderPhaseStrictModeWarningsInDEV(page);8 console.log(warnings);9 await browser.close();10})();
Using AI Code Generation
1const { flushRenderPhaseStrictModeWarningsInDEV } = require('playwright/lib/server/supplements/recorder/recorderSupplement');2flushRenderPhaseStrictModeWarningsInDEV();3const { chromium } = require('playwright');4(async () => {5 const browser = await chromium.launch();6 const context = await browser.newContext();7 const page = await context.newPage();8 await page.screenshot({ path: 'google.png' });9 awai brwser.close();10})();
Using AI Code Generation
1const playwright = require(‘playwright’);2playwright = require(‘playwright’);3constbrowser = await playwright[‘ium’].launch();4const page = await browser.newPage();5await page.route(‘**’, (route, request) => {6if (request.resourceType() === ‘xhr’) {7route.fulfill({8body: JSON.stringify({ success: true }),9});10} else {11route.continue();12}13});14await page.waitForSelector(‘[data-test-id=“title”]’);15await page.evalate(() => {16console.error(‘Warning: Somethingis not right’);17);18await page.flushRenderPhaseStrictModeWarningsInDEV();19await browser.close();20})();21const playwright = require(‘playwright’);22it(‘should flush React StrictMode warnings’, async () => {23const browser = awaitplaywright[‘chromium’].launch();24const page await browse.newPag();25await page.route(‘**’, (route, reest) => {26f (request.resouceTyp) === ‘xhr’) {27route.fulfill({28body: JSON.stringify({ success: true }),29});30} else {31route.continue();32}33});34await page.waitForSelector(‘[data-test-id=“title”]’);35await page.evaluate(() => {36console.error(‘Warning: Something is not right’);37});38await page.flushRenderPhaseStrictModeWarningsInDEV();39await browser.close();40});41await page.evaluate(() => {42window[‘__playwright__’] = {43flushRenderPhaseStrictModeWarningsInDEV: () => {},44};45});
Using AI Code Generation
1const { flushRenderPhaseStrictModeWarningsInDEV } = require(2describe('Test', () => {(async () => {3 it'should pass', () => {4 awaitpage.evaluate(5 coReantDOM.render(6 );7 });8 await flu hRenderPhaseStrictModeWarningsInDEV();9 });10});
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const owser = await playwright[‘chromium’].launch();4const page = await browser.newPage();5await page.route(‘**’, (route, request) => {6if (request.resourceType() === ‘xhr’) {
Using AI Code Generation
1const { flushRenderPhaseStrictModeWarningsInDEV } = require('playwright');2const { chromium } = require('playwright');3(async () => {4 const browser = await chromium.launch();5 const page = await browser.newPage();6 await page.evaluate(() => {7 ReactDOM.render(8 document.getElementById('root')9 );10 });11 const messages = await flushRenderPhaseStrictModeWarningsInDEV();12 console.log(messages);13 await browser.close();14})();15body: JSON.stringify({ success: true }),16});17} else {18route.continue();19}20});21await page.waitForSelector(‘[data-test-id=“title”]’);22await page.evaluate(() => {23console.error(‘Warning: Something is not right’);24});25await page.flushRenderPhaseStrictModeWarningsInDEV();26await browser.close();27})();28const playwright = require(‘playwright’);29it(‘should flush React StrictMode warnings’, async () => {30const browser = await playwright[‘chromium’].launch();31const page = await browser.newPage();32await page.route(‘**’, (route, request) => {33if (request.resourceType() === ‘xhr’) {34route.fulfill({35body: JSON.stringify({ success: true }),36});37} else {38route.continue();39}40});41await page.waitForSelector(‘[data-test-id=“title”]’);42await page.evaluate(() => {43console.error(‘Warning: Something is not right’);44});45await page.flushRenderPhaseStrictModeWarningsInDEV();46await browser.close();47});48await page.evaluate(() => {49window[‘__playwright__’] = {50flushRenderPhaseStrictModeWarningsInDEV: () => {},51};52});
Using AI Code Generation
1const { flushRenderPhaseStrictModeWarningsInDEV } = require('playwright');2describe('Test', () => {3 it('should pass', async () => {4 await page.evaluate(() => {5 ReactDOM.render(6 );7 });8 await flushRenderPhaseStrictModeWarningsInDEV();9 });10});
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const page = await browser.newPage();5 await page.evaluate(() => {6 });7 await page.flushRenderPhaseStrictModeWarningsInDEV();8 await browser.close();9})();
Using AI Code Generation
1const { flushRenderPhaseStrictModeWarningsInDEV } = require('playwright');2const { chromium } = require('playwright');3(async () => {4 const browser = await chromium.launch();5 const page = await browser.newPage();6 await page.evaluate(() => {7 ReactDOM.render(8 document.getElementById('root')9 );10 });11 const messages = await flushRenderPhaseStrictModeWarningsInDEV();12 console.log(messages);13 await browser.close();14})();
LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!