Hot reloading: Avoid stack overflow on wide trees (#34145)
Every sibling added to the stack here. Not sure this needs to be recursive at all but certainly for siblings this can just be a loop.
Sophie Alpert committed
Aug 9, 2025 at 08:02 UTC
cf6e502ed23ab3357e58965789324ddfa0e12821
1 file changed
+59
-59
packages/react-reconciler/src/ReactFiberHotReloading.js
+59
-59
@@ -261,74 +261,74 @@ function scheduleFibersWithFamiliesRecursively(
261
staleFamilies: Set<Family>,
262
): void {
263
if (__DEV__) {
264
- const {alternate, child, sibling, tag, type} = fiber;
264
+ do {
265
+ const {alternate, child, sibling, tag, type} = fiber;
266
266
- let candidateType = null;
267
- switch (tag) {
268
- case FunctionComponent:
269
- case SimpleMemoComponent:
270
- case ClassComponent:
271
- candidateType = type;
272
- break;
273
- case ForwardRef:
274
- candidateType = type.render;
275
- break;
276
- default:
277
- break;
278
- }
267
+ let candidateType = null;
268
+ switch (tag) {
269
+ case FunctionComponent:
270
+ case SimpleMemoComponent:
271
+ case ClassComponent:
272
+ candidateType = type;
273
+ break;
274
+ case ForwardRef:
275
+ candidateType = type.render;
276
+ break;
277
+ default:
278
+ break;
279
+ }
280
280
- if (resolveFamily === null) {
281
- throw new Error('Expected resolveFamily to be set during hot reload.');
282
- }
281
+ if (resolveFamily === null) {
282
+ throw new Error('Expected resolveFamily to be set during hot reload.');
283
+ }
284
284
- let needsRender = false;
285
- let needsRemount = false;
286
- if (candidateType !== null) {
287
- const family = resolveFamily(candidateType);
288
- if (family !== undefined) {
289
- if (staleFamilies.has(family)) {
290
- needsRemount = true;
291
- } else if (updatedFamilies.has(family)) {
292
- if (tag === ClassComponent) {
285
+ let needsRender = false;
286
+ let needsRemount = false;
287
+ if (candidateType !== null) {
288
+ const family = resolveFamily(candidateType);
289
+ if (family !== undefined) {
290
+ if (staleFamilies.has(family)) {
291
needsRemount = true;
294
- } else {
295
- needsRender = true;
292
+ } else if (updatedFamilies.has(family)) {
293
+ if (tag === ClassComponent) {
294
+ needsRemount = true;
295
+ } else {
296
+ needsRender = true;
297
+ }
298
}
299
}
300
}
299
- }
300
- if (failedBoundaries !== null) {
301
- if (
302
- failedBoundaries.has(fiber) ||
303
- // $FlowFixMe[incompatible-use] found when upgrading Flow
304
- (alternate !== null && failedBoundaries.has(alternate))
305
- ) {
306
- needsRemount = true;
301
+ if (failedBoundaries !== null) {
302
+ if (
303
+ failedBoundaries.has(fiber) ||
304
+ // $FlowFixMe[incompatible-use] found when upgrading Flow
305
+ (alternate !== null && failedBoundaries.has(alternate))
306
+ ) {
307
+ needsRemount = true;
308
+ }
309
}
308
- }
310
310
- if (needsRemount) {
311
- fiber._debugNeedsRemount = true;
312
- }
313
- if (needsRemount || needsRender) {
314
- const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
315
- if (root !== null) {
316
- scheduleUpdateOnFiber(root, fiber, SyncLane);
311
+ if (needsRemount) {
312
+ fiber._debugNeedsRemount = true;
313
}
318
- }
319
- if (child !== null && !needsRemount) {
320
- scheduleFibersWithFamiliesRecursively(
321
- child,
322
- updatedFamilies,
323
- staleFamilies,
324
- );
325
- }
326
- if (sibling !== null) {
327
- scheduleFibersWithFamiliesRecursively(
328
- sibling,
329
- updatedFamilies,
330
- staleFamilies,
331
- );
332
- }
314
+ if (needsRemount || needsRender) {
315
+ const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
316
+ if (root !== null) {
317
+ scheduleUpdateOnFiber(root, fiber, SyncLane);
318
+ }
319
+ }
320
+ if (child !== null && !needsRemount) {
321
+ scheduleFibersWithFamiliesRecursively(
322
+ child,
323
+ updatedFamilies,
324
+ staleFamilies,
325
+ );
326
+ }
327
+
328
+ if (sibling === null) {
329
+ break;
330
+ }
331
+ fiber = sibling;
332
+ } while (true);
333
}
334
}