@@ -69,7 +69,7 @@ function _assertThisInitialized(self) {
6969 return self;
7070}
7171
72- var ReactVersion = "18.3.0-www-classic-af595a64 ";
72+ var ReactVersion = "18.3.0-www-classic-4705a186 ";
7373
7474var LegacyRoot = 0;
7575var ConcurrentRoot = 1;
@@ -2165,7 +2165,7 @@ function createLaneMap(initial) {
21652165
21662166 return laneMap;
21672167}
2168- function markRootUpdated(root, updateLane) {
2168+ function markRootUpdated$1 (root, updateLane) {
21692169 root.pendingLanes |= updateLane; // If there are any suspended transitions, it's possible this new update
21702170 // could unblock them. Clear the suspended lanes so that we can try rendering
21712171 // them again.
@@ -2198,7 +2198,7 @@ function markRootSuspended$1(root, suspendedLanes) {
21982198 lanes &= ~lane;
21992199 }
22002200}
2201- function markRootPinged(root, pingedLanes) {
2201+ function markRootPinged$1 (root, pingedLanes) {
22022202 root.pingedLanes |= root.suspendedLanes & pingedLanes;
22032203}
22042204function markRootFinished(root, remainingLanes) {
@@ -7320,6 +7320,21 @@ function ensureRootIsScheduled(root) {
73207320 ReactCurrentActQueue$2.didScheduleLegacyUpdate = true;
73217321 }
73227322}
7323+
7324+ function unscheduleAllRoots() {
7325+ // This is only done in a fatal error situation, as a last resort to prevent
7326+ // an infinite render loop.
7327+ var root = firstScheduledRoot;
7328+
7329+ while (root !== null) {
7330+ var next = root.next;
7331+ root.next = null;
7332+ root = next;
7333+ }
7334+
7335+ firstScheduledRoot = lastScheduledRoot = null;
7336+ }
7337+
73237338function flushSyncWorkOnAllRoots() {
73247339 // This is allowed to be called synchronously, but the caller should check
73257340 // the execution context first.
@@ -7348,11 +7363,49 @@ function flushSyncWorkAcrossRoots_impl(onlyLegacy) {
73487363 var workInProgressRootRenderLanes = getWorkInProgressRootRenderLanes(); // There may or may not be synchronous work scheduled. Let's check.
73497364
73507365 var didPerformSomeWork;
7366+ var nestedUpdatePasses = 0;
73517367 var errors = null;
73527368 isFlushingWork = true;
73537369
73547370 do {
7355- didPerformSomeWork = false;
7371+ didPerformSomeWork = false; // This outer loop re-runs if performing sync work on a root spawns
7372+ // additional sync work. If it happens too many times, it's very likely
7373+ // caused by some sort of infinite update loop. We already have a loop guard
7374+ // in place that will trigger an error on the n+1th update, but it's
7375+ // possible for that error to get swallowed if the setState is called from
7376+ // an unexpected place, like during the render phase. So as an added
7377+ // precaution, we also use a guard here.
7378+ //
7379+ // Ideally, there should be no known way to trigger this synchronous loop.
7380+ // It's really just here as a safety net.
7381+ //
7382+ // This limit is slightly larger than the one that throws inside setState,
7383+ // because that one is preferable because it includes a componens stack.
7384+
7385+ if (++nestedUpdatePasses > 60) {
7386+ // This is a fatal error, so we'll unschedule all the roots.
7387+ unscheduleAllRoots(); // TODO: Change this error message to something different to distinguish
7388+ // it from the one that is thrown from setState. Those are less fatal
7389+ // because they usually will result in the bad component being unmounted,
7390+ // and an error boundary being triggered, rather than us having to
7391+ // forcibly stop the entire scheduler.
7392+
7393+ var infiniteUpdateError = new Error(
7394+ "Maximum update depth exceeded. This can happen when a component " +
7395+ "repeatedly calls setState inside componentWillUpdate or " +
7396+ "componentDidUpdate. React limits the number of nested updates to " +
7397+ "prevent infinite loops."
7398+ );
7399+
7400+ if (errors === null) {
7401+ errors = [infiniteUpdateError];
7402+ } else {
7403+ errors.push(infiniteUpdateError);
7404+ }
7405+
7406+ break;
7407+ }
7408+
73567409 var root = firstScheduledRoot;
73577410
73587411 while (root !== null) {
@@ -23668,7 +23721,13 @@ var workInProgressRootPingedLanes = NoLanes; // Errors that are thrown during th
2366823721var workInProgressRootConcurrentErrors = null; // These are errors that we recovered from without surfacing them to the UI.
2366923722// We will log them once the tree commits.
2367023723
23671- var workInProgressRootRecoverableErrors = null; // The most recent time we either committed a fallback, or when a fallback was
23724+ var workInProgressRootRecoverableErrors = null; // Tracks when an update occurs during the render phase.
23725+
23726+ var workInProgressRootDidIncludeRecursiveRenderUpdate = false; // Thacks when an update occurs during the commit phase. It's a separate
23727+ // variable from the one for renders because the commit phase may run
23728+ // concurrently to a render phase.
23729+
23730+ var didIncludeCommitPhaseUpdate = false; // The most recent time we either committed a fallback, or when a fallback was
2367223731// filled in with the resolved UI. This lets us throttle the appearance of new
2367323732// content as it streams in, to minimize jank.
2367423733// TODO: Think of a better name for this variable?
@@ -24347,7 +24406,8 @@ function finishConcurrentRender(root, exitStatus, finishedWork, lanes) {
2434724406 commitRoot(
2434824407 root,
2434924408 workInProgressRootRecoverableErrors,
24350- workInProgressTransitions
24409+ workInProgressTransitions,
24410+ workInProgressRootDidIncludeRecursiveRenderUpdate
2435124411 );
2435224412 } else {
2435324413 if (
@@ -24380,6 +24440,7 @@ function finishConcurrentRender(root, exitStatus, finishedWork, lanes) {
2438024440 finishedWork,
2438124441 workInProgressRootRecoverableErrors,
2438224442 workInProgressTransitions,
24443+ workInProgressRootDidIncludeRecursiveRenderUpdate,
2438324444 lanes
2438424445 ),
2438524446 msUntilTimeout
@@ -24393,6 +24454,7 @@ function finishConcurrentRender(root, exitStatus, finishedWork, lanes) {
2439324454 finishedWork,
2439424455 workInProgressRootRecoverableErrors,
2439524456 workInProgressTransitions,
24457+ workInProgressRootDidIncludeRecursiveRenderUpdate,
2439624458 lanes
2439724459 );
2439824460 }
@@ -24403,6 +24465,7 @@ function commitRootWhenReady(
2440324465 finishedWork,
2440424466 recoverableErrors,
2440524467 transitions,
24468+ didIncludeRenderPhaseUpdate,
2440624469 lanes
2440724470) {
2440824471 // TODO: Combine retry throttling with Suspensey commits. Right now they run
@@ -24426,14 +24489,20 @@ function commitRootWhenReady(
2442624489 // us that it's ready. This will be canceled if we start work on the
2442724490 // root again.
2442824491 root.cancelPendingCommit = schedulePendingCommit(
24429- commitRoot.bind(null, root, recoverableErrors, transitions)
24492+ commitRoot.bind(
24493+ null,
24494+ root,
24495+ recoverableErrors,
24496+ transitions,
24497+ didIncludeRenderPhaseUpdate
24498+ )
2443024499 );
2443124500 markRootSuspended(root, lanes);
2443224501 return;
2443324502 }
2443424503 } // Otherwise, commit immediately.
2443524504
24436- commitRoot(root, recoverableErrors, transitions);
24505+ commitRoot(root, recoverableErrors, transitions, didIncludeRenderPhaseUpdate );
2443724506}
2443824507
2443924508function isRenderConsistentWithExternalStores(finishedWork) {
@@ -24496,18 +24565,49 @@ function isRenderConsistentWithExternalStores(finishedWork) {
2449624565 // eslint-disable-next-line no-unreachable
2449724566
2449824567 return true;
24568+ } // The extra indirections around markRootUpdated and markRootSuspended is
24569+ // needed to avoid a circular dependency between this module and
24570+ // ReactFiberLane. There's probably a better way to split up these modules and
24571+ // avoid this problem. Perhaps all the root-marking functions should move into
24572+ // the work loop.
24573+
24574+ function markRootUpdated(root, updatedLanes) {
24575+ markRootUpdated$1(root, updatedLanes); // Check for recursive updates
24576+
24577+ if (executionContext & RenderContext) {
24578+ workInProgressRootDidIncludeRecursiveRenderUpdate = true;
24579+ } else if (executionContext & CommitContext) {
24580+ didIncludeCommitPhaseUpdate = true;
24581+ }
24582+
24583+ throwIfInfiniteUpdateLoopDetected();
24584+ }
24585+
24586+ function markRootPinged(root, pingedLanes) {
24587+ markRootPinged$1(root, pingedLanes); // Check for recursive pings. Pings are conceptually different from updates in
24588+ // other contexts but we call it an "update" in this context because
24589+ // repeatedly pinging a suspended render can cause a recursive render loop.
24590+ // The relevant property is that it can result in a new render attempt
24591+ // being scheduled.
24592+
24593+ if (executionContext & RenderContext) {
24594+ workInProgressRootDidIncludeRecursiveRenderUpdate = true;
24595+ } else if (executionContext & CommitContext) {
24596+ didIncludeCommitPhaseUpdate = true;
24597+ }
24598+
24599+ throwIfInfiniteUpdateLoopDetected();
2449924600}
2450024601
2450124602function markRootSuspended(root, suspendedLanes) {
2450224603 // When suspending, we should always exclude lanes that were pinged or (more
2450324604 // rarely, since we try to avoid it) updated during the render phase.
24504- // TODO: Lol maybe there's a better way to factor this besides this
24505- // obnoxiously named function :)
2450624605 suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);
2450724606 suspendedLanes = removeLanes(
2450824607 suspendedLanes,
2450924608 workInProgressRootInterleavedUpdatedLanes
2451024609 );
24610+
2451124611 markRootSuspended$1(root, suspendedLanes);
2451224612} // This is the entry point for synchronous tasks that don't go
2451324613// through Scheduler
@@ -24578,7 +24678,8 @@ function performSyncWorkOnRoot(root) {
2457824678 commitRoot(
2457924679 root,
2458024680 workInProgressRootRecoverableErrors,
24581- workInProgressTransitions
24681+ workInProgressTransitions,
24682+ workInProgressRootDidIncludeRecursiveRenderUpdate
2458224683 ); // Before exiting, make sure there's a callback scheduled for the next
2458324684 // pending level.
2458424685
@@ -24704,6 +24805,7 @@ function prepareFreshStack(root, lanes) {
2470424805 workInProgressRootPingedLanes = NoLanes;
2470524806 workInProgressRootConcurrentErrors = null;
2470624807 workInProgressRootRecoverableErrors = null;
24808+ workInProgressRootDidIncludeRecursiveRenderUpdate = false;
2470724809 finishQueueingConcurrentUpdates();
2470824810
2470924811 {
@@ -25706,7 +25808,12 @@ function unwindUnitOfWork(unitOfWork) {
2570625808 workInProgress = null;
2570725809}
2570825810
25709- function commitRoot(root, recoverableErrors, transitions) {
25811+ function commitRoot(
25812+ root,
25813+ recoverableErrors,
25814+ transitions,
25815+ didIncludeRenderPhaseUpdate
25816+ ) {
2571025817 // TODO: This no longer makes any sense. We already wrap the mutation and
2571125818 // layout phases. Should be able to remove.
2571225819 var previousUpdateLanePriority = getCurrentUpdatePriority();
@@ -25719,6 +25826,7 @@ function commitRoot(root, recoverableErrors, transitions) {
2571925826 root,
2572025827 recoverableErrors,
2572125828 transitions,
25829+ didIncludeRenderPhaseUpdate,
2572225830 previousUpdateLanePriority
2572325831 );
2572425832 } finally {
@@ -25733,6 +25841,7 @@ function commitRootImpl(
2573325841 root,
2573425842 recoverableErrors,
2573525843 transitions,
25844+ didIncludeRenderPhaseUpdate,
2573625845 renderPriorityLevel
2573725846) {
2573825847 do {
@@ -25808,7 +25917,9 @@ function commitRootImpl(
2580825917
2580925918 var concurrentlyUpdatedLanes = getConcurrentlyUpdatedLanes();
2581025919 remainingLanes = mergeLanes(remainingLanes, concurrentlyUpdatedLanes);
25811- markRootFinished(root, remainingLanes);
25920+ markRootFinished(root, remainingLanes); // Reset this before firing side effects so we can detect recursive updates.
25921+
25922+ didIncludeCommitPhaseUpdate = false;
2581225923
2581325924 if (root === workInProgressRoot) {
2581425925 // We can reset these now that they are finished.
@@ -26027,7 +26138,18 @@ function commitRootImpl(
2602726138
2602826139 remainingLanes = root.pendingLanes;
2602926140
26030- if (includesSyncLane(remainingLanes)) {
26141+ if (
26142+ // Check if there was a recursive update spawned by this render, in either
26143+ // the render phase or the commit phase. We track these explicitly because
26144+ // we can't infer from the remaining lanes alone.
26145+ didIncludeCommitPhaseUpdate ||
26146+ didIncludeRenderPhaseUpdate || // As an additional precaution, we also check if there's any remaining sync
26147+ // work. Theoretically this should be unreachable but if there's a mistake
26148+ // in React it helps to be overly defensive given how hard it is to debug
26149+ // those scenarios otherwise. This won't catch recursive async updates,
26150+ // though, which is why we check the flags above first.
26151+ includesSyncLane(remainingLanes)
26152+ ) {
2603126153 {
2603226154 markNestedUpdateScheduled();
2603326155 } // Count the number of times the root synchronously re-renders without
@@ -26522,6 +26644,18 @@ function throwIfInfiniteUpdateLoopDetected() {
2652226644 nestedPassiveUpdateCount = 0;
2652326645 rootWithNestedUpdates = null;
2652426646 rootWithPassiveNestedUpdates = null;
26647+
26648+ if (executionContext & RenderContext && workInProgressRoot !== null) {
26649+ // We're in the render phase. Disable the concurrent error recovery
26650+ // mechanism to ensure that the error we're about to throw gets handled.
26651+ // We need it to trigger the nearest error boundary so that the infinite
26652+ // update loop is broken.
26653+ workInProgressRoot.errorRecoveryDisabledLanes = mergeLanes(
26654+ workInProgressRoot.errorRecoveryDisabledLanes,
26655+ workInProgressRootRenderLanes
26656+ );
26657+ }
26658+
2652526659 throw new Error(
2652626660 "Maximum update depth exceeded. This can happen when a component " +
2652726661 "repeatedly calls setState inside componentWillUpdate or " +
0 commit comments