Skip to content

Commit d86fbe9

Browse files
committed
Revert "Fix: Detect infinite update loops caused by render phase updates (#26625)" (#27027)
This reverts commit 822386f252fd1f0e949efa904a1ed790133329f7. This broke a number of tests when synced internally. We'll need to investigate the breakages before relanding this. DiffTrain build for [7f362de1588d98438787d652941533e21f2f332d](facebook/react@7f362de)
1 parent af184d1 commit d86fbe9

20 files changed

+460
-1912
lines changed

compiled/facebook-www/REVISION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
fc801116c80b68f7ebdaf66ac77d5f2dcd9e50eb
1+
7f362de1588d98438787d652941533e21f2f332d

compiled/facebook-www/React-dev.classic.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ if (
2727
}
2828
"use strict";
2929

30-
var ReactVersion = "18.3.0-www-classic-a3146e4f";
30+
var ReactVersion = "18.3.0-www-classic-60aa7d44";
3131

3232
// ATTENTION
3333
// When adding new symbols to this file,

compiled/facebook-www/React-dev.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ if (
2727
}
2828
"use strict";
2929

30-
var ReactVersion = "18.3.0-www-modern-f95d3ddd";
30+
var ReactVersion = "18.3.0-www-modern-fec65964";
3131

3232
// ATTENTION
3333
// When adding new symbols to this file,

compiled/facebook-www/React-prod.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,4 +622,4 @@ exports.useSyncExternalStore = function (
622622
);
623623
};
624624
exports.useTransition = useTransition;
625-
exports.version = "18.3.0-www-modern-0b35a427";
625+
exports.version = "18.3.0-www-modern-e397df4a";

compiled/facebook-www/ReactART-dev.classic.js

Lines changed: 14 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ function _assertThisInitialized(self) {
6969
return self;
7070
}
7171

72-
var ReactVersion = "18.3.0-www-classic-a18a6ba5";
72+
var ReactVersion = "18.3.0-www-classic-9da53968";
7373

7474
var LegacyRoot = 0;
7575
var ConcurrentRoot = 1;
@@ -2165,7 +2165,7 @@ function createLaneMap(initial) {
21652165

21662166
return laneMap;
21672167
}
2168-
function markRootUpdated$1(root, updateLane) {
2168+
function markRootUpdated(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$1(root, pingedLanes) {
2201+
function markRootPinged(root, pingedLanes) {
22022202
root.pingedLanes |= root.suspendedLanes & pingedLanes;
22032203
}
22042204
function markRootFinished(root, remainingLanes) {
@@ -7347,21 +7347,6 @@ function ensureRootIsScheduled(root) {
73477347
ReactCurrentActQueue$2.didScheduleLegacyUpdate = true;
73487348
}
73497349
}
7350-
7351-
function unscheduleAllRoots() {
7352-
// This is only done in a fatal error situation, as a last resort to prevent
7353-
// an infinite render loop.
7354-
var root = firstScheduledRoot;
7355-
7356-
while (root !== null) {
7357-
var next = root.next;
7358-
root.next = null;
7359-
root = next;
7360-
}
7361-
7362-
firstScheduledRoot = lastScheduledRoot = null;
7363-
}
7364-
73657350
function flushSyncWorkOnAllRoots() {
73667351
// This is allowed to be called synchronously, but the caller should check
73677352
// the execution context first.
@@ -7390,49 +7375,11 @@ function flushSyncWorkAcrossRoots_impl(onlyLegacy) {
73907375
var workInProgressRootRenderLanes = getWorkInProgressRootRenderLanes(); // There may or may not be synchronous work scheduled. Let's check.
73917376

73927377
var didPerformSomeWork;
7393-
var nestedUpdatePasses = 0;
73947378
var errors = null;
73957379
isFlushingWork = true;
73967380

73977381
do {
7398-
didPerformSomeWork = false; // This outer loop re-runs if performing sync work on a root spawns
7399-
// additional sync work. If it happens too many times, it's very likely
7400-
// caused by some sort of infinite update loop. We already have a loop guard
7401-
// in place that will trigger an error on the n+1th update, but it's
7402-
// possible for that error to get swallowed if the setState is called from
7403-
// an unexpected place, like during the render phase. So as an added
7404-
// precaution, we also use a guard here.
7405-
//
7406-
// Ideally, there should be no known way to trigger this synchronous loop.
7407-
// It's really just here as a safety net.
7408-
//
7409-
// This limit is slightly larger than the one that throws inside setState,
7410-
// because that one is preferable because it includes a componens stack.
7411-
7412-
if (++nestedUpdatePasses > 60) {
7413-
// This is a fatal error, so we'll unschedule all the roots.
7414-
unscheduleAllRoots(); // TODO: Change this error message to something different to distinguish
7415-
// it from the one that is thrown from setState. Those are less fatal
7416-
// because they usually will result in the bad component being unmounted,
7417-
// and an error boundary being triggered, rather than us having to
7418-
// forcibly stop the entire scheduler.
7419-
7420-
var infiniteUpdateError = new Error(
7421-
"Maximum update depth exceeded. This can happen when a component " +
7422-
"repeatedly calls setState inside componentWillUpdate or " +
7423-
"componentDidUpdate. React limits the number of nested updates to " +
7424-
"prevent infinite loops."
7425-
);
7426-
7427-
if (errors === null) {
7428-
errors = [infiniteUpdateError];
7429-
} else {
7430-
errors.push(infiniteUpdateError);
7431-
}
7432-
7433-
break;
7434-
}
7435-
7382+
didPerformSomeWork = false;
74367383
var root = firstScheduledRoot;
74377384

74387385
while (root !== null) {
@@ -23748,13 +23695,7 @@ var workInProgressRootPingedLanes = NoLanes; // Errors that are thrown during th
2374823695
var workInProgressRootConcurrentErrors = null; // These are errors that we recovered from without surfacing them to the UI.
2374923696
// We will log them once the tree commits.
2375023697

23751-
var workInProgressRootRecoverableErrors = null; // Tracks when an update occurs during the render phase.
23752-
23753-
var workInProgressRootDidIncludeRecursiveRenderUpdate = false; // Thacks when an update occurs during the commit phase. It's a separate
23754-
// variable from the one for renders because the commit phase may run
23755-
// concurrently to a render phase.
23756-
23757-
var didIncludeCommitPhaseUpdate = false; // The most recent time we either committed a fallback, or when a fallback was
23698+
var workInProgressRootRecoverableErrors = null; // The most recent time we either committed a fallback, or when a fallback was
2375823699
// filled in with the resolved UI. This lets us throttle the appearance of new
2375923700
// content as it streams in, to minimize jank.
2376023701
// TODO: Think of a better name for this variable?
@@ -24433,8 +24374,7 @@ function finishConcurrentRender(root, exitStatus, finishedWork, lanes) {
2443324374
commitRoot(
2443424375
root,
2443524376
workInProgressRootRecoverableErrors,
24436-
workInProgressTransitions,
24437-
workInProgressRootDidIncludeRecursiveRenderUpdate
24377+
workInProgressTransitions
2443824378
);
2443924379
} else {
2444024380
if (
@@ -24467,7 +24407,6 @@ function finishConcurrentRender(root, exitStatus, finishedWork, lanes) {
2446724407
finishedWork,
2446824408
workInProgressRootRecoverableErrors,
2446924409
workInProgressTransitions,
24470-
workInProgressRootDidIncludeRecursiveRenderUpdate,
2447124410
lanes
2447224411
),
2447324412
msUntilTimeout
@@ -24481,7 +24420,6 @@ function finishConcurrentRender(root, exitStatus, finishedWork, lanes) {
2448124420
finishedWork,
2448224421
workInProgressRootRecoverableErrors,
2448324422
workInProgressTransitions,
24484-
workInProgressRootDidIncludeRecursiveRenderUpdate,
2448524423
lanes
2448624424
);
2448724425
}
@@ -24492,7 +24430,6 @@ function commitRootWhenReady(
2449224430
finishedWork,
2449324431
recoverableErrors,
2449424432
transitions,
24495-
didIncludeRenderPhaseUpdate,
2449624433
lanes
2449724434
) {
2449824435
// TODO: Combine retry throttling with Suspensey commits. Right now they run
@@ -24516,20 +24453,14 @@ function commitRootWhenReady(
2451624453
// us that it's ready. This will be canceled if we start work on the
2451724454
// root again.
2451824455
root.cancelPendingCommit = schedulePendingCommit(
24519-
commitRoot.bind(
24520-
null,
24521-
root,
24522-
recoverableErrors,
24523-
transitions,
24524-
didIncludeRenderPhaseUpdate
24525-
)
24456+
commitRoot.bind(null, root, recoverableErrors, transitions)
2452624457
);
2452724458
markRootSuspended(root, lanes);
2452824459
return;
2452924460
}
2453024461
} // Otherwise, commit immediately.
2453124462

24532-
commitRoot(root, recoverableErrors, transitions, didIncludeRenderPhaseUpdate);
24463+
commitRoot(root, recoverableErrors, transitions);
2453324464
}
2453424465

2453524466
function isRenderConsistentWithExternalStores(finishedWork) {
@@ -24592,49 +24523,18 @@ function isRenderConsistentWithExternalStores(finishedWork) {
2459224523
// eslint-disable-next-line no-unreachable
2459324524

2459424525
return true;
24595-
} // The extra indirections around markRootUpdated and markRootSuspended is
24596-
// needed to avoid a circular dependency between this module and
24597-
// ReactFiberLane. There's probably a better way to split up these modules and
24598-
// avoid this problem. Perhaps all the root-marking functions should move into
24599-
// the work loop.
24600-
24601-
function markRootUpdated(root, updatedLanes) {
24602-
markRootUpdated$1(root, updatedLanes); // Check for recursive updates
24603-
24604-
if (executionContext & RenderContext) {
24605-
workInProgressRootDidIncludeRecursiveRenderUpdate = true;
24606-
} else if (executionContext & CommitContext) {
24607-
didIncludeCommitPhaseUpdate = true;
24608-
}
24609-
24610-
throwIfInfiniteUpdateLoopDetected();
24611-
}
24612-
24613-
function markRootPinged(root, pingedLanes) {
24614-
markRootPinged$1(root, pingedLanes); // Check for recursive pings. Pings are conceptually different from updates in
24615-
// other contexts but we call it an "update" in this context because
24616-
// repeatedly pinging a suspended render can cause a recursive render loop.
24617-
// The relevant property is that it can result in a new render attempt
24618-
// being scheduled.
24619-
24620-
if (executionContext & RenderContext) {
24621-
workInProgressRootDidIncludeRecursiveRenderUpdate = true;
24622-
} else if (executionContext & CommitContext) {
24623-
didIncludeCommitPhaseUpdate = true;
24624-
}
24625-
24626-
throwIfInfiniteUpdateLoopDetected();
2462724526
}
2462824527

2462924528
function markRootSuspended(root, suspendedLanes) {
2463024529
// When suspending, we should always exclude lanes that were pinged or (more
2463124530
// rarely, since we try to avoid it) updated during the render phase.
24531+
// TODO: Lol maybe there's a better way to factor this besides this
24532+
// obnoxiously named function :)
2463224533
suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);
2463324534
suspendedLanes = removeLanes(
2463424535
suspendedLanes,
2463524536
workInProgressRootInterleavedUpdatedLanes
2463624537
);
24637-
2463824538
markRootSuspended$1(root, suspendedLanes);
2463924539
} // This is the entry point for synchronous tasks that don't go
2464024540
// through Scheduler
@@ -24705,8 +24605,7 @@ function performSyncWorkOnRoot(root) {
2470524605
commitRoot(
2470624606
root,
2470724607
workInProgressRootRecoverableErrors,
24708-
workInProgressTransitions,
24709-
workInProgressRootDidIncludeRecursiveRenderUpdate
24608+
workInProgressTransitions
2471024609
); // Before exiting, make sure there's a callback scheduled for the next
2471124610
// pending level.
2471224611

@@ -24832,7 +24731,6 @@ function prepareFreshStack(root, lanes) {
2483224731
workInProgressRootPingedLanes = NoLanes;
2483324732
workInProgressRootConcurrentErrors = null;
2483424733
workInProgressRootRecoverableErrors = null;
24835-
workInProgressRootDidIncludeRecursiveRenderUpdate = false;
2483624734
finishQueueingConcurrentUpdates();
2483724735

2483824736
{
@@ -25853,12 +25751,7 @@ function unwindUnitOfWork(unitOfWork) {
2585325751
workInProgress = null;
2585425752
}
2585525753

25856-
function commitRoot(
25857-
root,
25858-
recoverableErrors,
25859-
transitions,
25860-
didIncludeRenderPhaseUpdate
25861-
) {
25754+
function commitRoot(root, recoverableErrors, transitions) {
2586225755
// TODO: This no longer makes any sense. We already wrap the mutation and
2586325756
// layout phases. Should be able to remove.
2586425757
var previousUpdateLanePriority = getCurrentUpdatePriority();
@@ -25871,7 +25764,6 @@ function commitRoot(
2587125764
root,
2587225765
recoverableErrors,
2587325766
transitions,
25874-
didIncludeRenderPhaseUpdate,
2587525767
previousUpdateLanePriority
2587625768
);
2587725769
} finally {
@@ -25886,7 +25778,6 @@ function commitRootImpl(
2588625778
root,
2588725779
recoverableErrors,
2588825780
transitions,
25889-
didIncludeRenderPhaseUpdate,
2589025781
renderPriorityLevel
2589125782
) {
2589225783
do {
@@ -25962,9 +25853,7 @@ function commitRootImpl(
2596225853

2596325854
var concurrentlyUpdatedLanes = getConcurrentlyUpdatedLanes();
2596425855
remainingLanes = mergeLanes(remainingLanes, concurrentlyUpdatedLanes);
25965-
markRootFinished(root, remainingLanes); // Reset this before firing side effects so we can detect recursive updates.
25966-
25967-
didIncludeCommitPhaseUpdate = false;
25856+
markRootFinished(root, remainingLanes);
2596825857

2596925858
if (root === workInProgressRoot) {
2597025859
// We can reset these now that they are finished.
@@ -26183,18 +26072,7 @@ function commitRootImpl(
2618326072

2618426073
remainingLanes = root.pendingLanes;
2618526074

26186-
if (
26187-
// Check if there was a recursive update spawned by this render, in either
26188-
// the render phase or the commit phase. We track these explicitly because
26189-
// we can't infer from the remaining lanes alone.
26190-
didIncludeCommitPhaseUpdate ||
26191-
didIncludeRenderPhaseUpdate || // As an additional precaution, we also check if there's any remaining sync
26192-
// work. Theoretically this should be unreachable but if there's a mistake
26193-
// in React it helps to be overly defensive given how hard it is to debug
26194-
// those scenarios otherwise. This won't catch recursive async updates,
26195-
// though, which is why we check the flags above first.
26196-
includesSyncLane(remainingLanes)
26197-
) {
26075+
if (includesSyncLane(remainingLanes)) {
2619826076
{
2619926077
markNestedUpdateScheduled();
2620026078
} // Count the number of times the root synchronously re-renders without
@@ -26689,18 +26567,6 @@ function throwIfInfiniteUpdateLoopDetected() {
2668926567
nestedPassiveUpdateCount = 0;
2669026568
rootWithNestedUpdates = null;
2669126569
rootWithPassiveNestedUpdates = null;
26692-
26693-
if (executionContext & RenderContext && workInProgressRoot !== null) {
26694-
// We're in the render phase. Disable the concurrent error recovery
26695-
// mechanism to ensure that the error we're about to throw gets handled.
26696-
// We need it to trigger the nearest error boundary so that the infinite
26697-
// update loop is broken.
26698-
workInProgressRoot.errorRecoveryDisabledLanes = mergeLanes(
26699-
workInProgressRoot.errorRecoveryDisabledLanes,
26700-
workInProgressRootRenderLanes
26701-
);
26702-
}
26703-
2670426570
throw new Error(
2670526571
"Maximum update depth exceeded. This can happen when a component " +
2670626572
"repeatedly calls setState inside componentWillUpdate or " +

0 commit comments

Comments
 (0)