diff --git a/packages/react-reconciler/src/ReactFiberConcurrentUpdates.new.js b/packages/react-reconciler/src/ReactFiberConcurrentUpdates.new.js index ee35a11640..74e19f40b4 100644 --- a/packages/react-reconciler/src/ReactFiberConcurrentUpdates.new.js +++ b/packages/react-reconciler/src/ReactFiberConcurrentUpdates.new.js @@ -19,7 +19,10 @@ import type { import type {Lane, Lanes} from './ReactFiberLane.new'; import type {OffscreenInstance} from './ReactFiberOffscreenComponent'; -import {warnAboutUpdateOnNotYetMountedFiberInDEV} from './ReactFiberWorkLoop.new'; +import { + warnAboutUpdateOnNotYetMountedFiberInDEV, + throwIfInfiniteUpdateLoopDetected, +} from './ReactFiberWorkLoop.new'; import { NoLane, NoLanes, @@ -207,6 +210,13 @@ function markUpdateLaneFromFiberToRoot( } function getRootForUpdatedFiber(sourceFiber: Fiber): FiberRoot | null { + // TODO: We will detect and infinite update loop and throw even if this fiber + // has already unmounted. This isn't really necessary but it happens to be the + // current behavior we've used for several release cycles. Consider not + // performing this check if the updated fiber already unmounted, since it's + // not possible for that to cause an infinite update loop. + throwIfInfiniteUpdateLoopDetected(); + // When a setState happens, we must ensure the root is scheduled. Because // update queues do not have a backpointer to the root, the only way to do // this currently is to walk up the return path. This used to not be a big diff --git a/packages/react-reconciler/src/ReactFiberConcurrentUpdates.old.js b/packages/react-reconciler/src/ReactFiberConcurrentUpdates.old.js index 8f3a11c938..42b6520238 100644 --- a/packages/react-reconciler/src/ReactFiberConcurrentUpdates.old.js +++ b/packages/react-reconciler/src/ReactFiberConcurrentUpdates.old.js @@ -18,7 +18,10 @@ import type { } from './ReactFiberClassUpdateQueue.old'; import type {Lane} from './ReactFiberLane.old'; -import {warnAboutUpdateOnNotYetMountedFiberInDEV} from './ReactFiberWorkLoop.old'; +import { + warnAboutUpdateOnNotYetMountedFiberInDEV, + throwIfInfiniteUpdateLoopDetected, +} from './ReactFiberWorkLoop.old'; import {mergeLanes} from './ReactFiberLane.old'; import {NoFlags, Placement, Hydrating} from './ReactFiberFlags'; import {HostRoot} from './ReactWorkTags'; @@ -143,6 +146,13 @@ function markUpdateLaneFromFiberToRoot( sourceFiber: Fiber, lane: Lane, ): FiberRoot | null { + // TODO: We will detect and infinite update loop and throw even if this fiber + // has already unmounted. This isn't really necessary but it happens to be the + // current behavior we've used for several release cycles. Consider not + // performing this check if the updated fiber already unmounted, since it's + // not possible for that to cause an infinite update loop. + throwIfInfiniteUpdateLoopDetected(); + // Update the source fiber's lanes sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane); let alternate = sourceFiber.alternate; diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index e7e95b2694..21f9d17850 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -536,8 +536,6 @@ export function scheduleUpdateOnFiber( lane: Lane, eventTime: number, ) { - checkForNestedUpdates(); - if (__DEV__) { if (isRunningInsertionEffect) { console.error('useInsertionEffect must not schedule updates.'); @@ -2782,10 +2780,12 @@ function jnd(timeElapsed: number) { : ceil(timeElapsed / 1960) * 1960; } -function checkForNestedUpdates() { +export function throwIfInfiniteUpdateLoopDetected() { if (nestedUpdateCount > NESTED_UPDATE_LIMIT) { nestedUpdateCount = 0; + nestedPassiveUpdateCount = 0; rootWithNestedUpdates = null; + rootWithPassiveNestedUpdates = null; throw new Error( 'Maximum update depth exceeded. This can happen when a component ' + diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index dc51787739..4f0af43870 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -531,8 +531,6 @@ export function scheduleUpdateOnFiber( lane: Lane, eventTime: number, ) { - checkForNestedUpdates(); - if (__DEV__) { if (isRunningInsertionEffect) { console.error('useInsertionEffect must not schedule updates.'); @@ -2771,7 +2769,7 @@ function jnd(timeElapsed: number) { : ceil(timeElapsed / 1960) * 1960; } -function checkForNestedUpdates() { +export function throwIfInfiniteUpdateLoopDetected() { if (nestedUpdateCount > NESTED_UPDATE_LIMIT) { nestedUpdateCount = 0; rootWithNestedUpdates = null;