1094
hook.getFiberRoots(rendererID).forEach(root => {
1095
currentRootID = getOrGenerateFiberInstance(root.current).id;
1096
setRootPseudoKey(currentRootID, root.current);
1097
- mountFiberRecursively(root.current, null, false, false);
1097
+ mountFiberRecursively(root.current, null, false);
1098
flushPendingEvents(root);
1099
currentRootID = -1;
1100
});
2228
}
2229
}
2230
2231
- function mountFiberRecursively(
2231
+ function mountChildrenRecursively(
2232
firstChild: Fiber,
2233
parentInstance: DevToolsInstance | null,
2234
- traverseSiblings: boolean,
2234
traceNearestHostComponentUpdate: boolean,
2236
- ) {
2235
+ ): void {
2236
// Iterate over siblings rather than recursing.
2237
// This reduces the chance of stack overflow for wide trees (e.g. lists with many items).
2238
let fiber: Fiber | null = firstChild;
2239
while (fiber !== null) {
2241
- // Generate an ID even for filtered Fibers, in case it's needed later (e.g. for Profiling).
2242
- // TODO: Do we really need to do this eagerly?
2243
- getOrGenerateFiberInstance(fiber);
2240
+ mountFiberRecursively(
2241
+ fiber,
2242
+ parentInstance,
2243
+ traceNearestHostComponentUpdate,
2244
+ );
2245
+ fiber = fiber.sibling;
2246
+ }
2247
+ }
2248
2245
- if (__DEBUG__) {
2246
- debug('mountFiberRecursively()', fiber, parentInstance);
2247
- }
2249
+ function mountFiberRecursively(
2250
+ fiber: Fiber,
2251
+ parentInstance: DevToolsInstance | null,
2252
+ traceNearestHostComponentUpdate: boolean,
2253
+ ): void {
2254
+ // Generate an ID even for filtered Fibers, in case it's needed later (e.g. for Profiling).
2255
+ // TODO: Do we really need to do this eagerly?
2256
+ getOrGenerateFiberInstance(fiber);
2257
2249
- // If we have the tree selection from previous reload, try to match this Fiber.
2250
- // Also remember whether to do the same for siblings.
2251
- const mightSiblingsBeOnTrackedPath =
2252
- updateTrackedPathStateBeforeMount(fiber);
2253
-
2254
- const shouldIncludeInTree = !shouldFilterFiber(fiber);
2255
- const newParentInstance = shouldIncludeInTree
2256
- ? recordMount(fiber, parentInstance)
2257
- : parentInstance;
2258
-
2259
- if (traceUpdatesEnabled) {
2260
- if (traceNearestHostComponentUpdate) {
2261
- const elementType = getElementTypeForFiber(fiber);
2262
- // If an ancestor updated, we should mark the nearest host nodes for highlighting.
2263
- if (elementType === ElementTypeHostComponent) {
2264
- traceUpdatesForNodes.add(fiber.stateNode);
2265
- traceNearestHostComponentUpdate = false;
2266
- }
2267
- }
2258
+ if (__DEBUG__) {
2259
+ debug('mountFiberRecursively()', fiber, parentInstance);
2260
+ }
2261
2269
- // We intentionally do not re-enable the traceNearestHostComponentUpdate flag in this branch,
2270
- // because we don't want to highlight every host node inside of a newly mounted subtree.
2262
+ // If we have the tree selection from previous reload, try to match this Fiber.
2263
+ // Also remember whether to do the same for siblings.
2264
+ const mightSiblingsBeOnTrackedPath =
2265
+ updateTrackedPathStateBeforeMount(fiber);
2266
+
2267
+ const shouldIncludeInTree = !shouldFilterFiber(fiber);
2268
+ const newParentInstance = shouldIncludeInTree
2269
+ ? recordMount(fiber, parentInstance)
2270
+ : parentInstance;
2271
+
2272
+ if (traceUpdatesEnabled) {
2273
+ if (traceNearestHostComponentUpdate) {
2274
+ const elementType = getElementTypeForFiber(fiber);
2275
+ // If an ancestor updated, we should mark the nearest host nodes for highlighting.
2276
+ if (elementType === ElementTypeHostComponent) {
2277
+ traceUpdatesForNodes.add(fiber.stateNode);
2278
+ traceNearestHostComponentUpdate = false;
2279
+ }
2280
}
2281
2273
- const isSuspense = fiber.tag === ReactTypeOfWork.SuspenseComponent;
2274
- if (isSuspense) {
2275
- const isTimedOut = fiber.memoizedState !== null;
2276
- if (isTimedOut) {
2277
- // Special case: if Suspense mounts in a timed-out state,
2278
- // get the fallback child from the inner fragment and mount
2279
- // it as if it was our own child. Updates handle this too.
2280
- const primaryChildFragment = fiber.child;
2281
- const fallbackChildFragment = primaryChildFragment
2282
- ? primaryChildFragment.sibling
2283
- : null;
2284
- const fallbackChild = fallbackChildFragment
2285
- ? fallbackChildFragment.child
2286
- : null;
2287
- if (fallbackChild !== null) {
2288
- mountFiberRecursively(
2289
- fallbackChild,
2290
- newParentInstance,
2291
- true,
2292
- traceNearestHostComponentUpdate,
2293
- );
2294
- }
2295
- } else {
2296
- let primaryChild: Fiber | null = null;
2297
- const areSuspenseChildrenConditionallyWrapped =
2298
- OffscreenComponent === -1;
2299
- if (areSuspenseChildrenConditionallyWrapped) {
2300
- primaryChild = fiber.child;
2301
- } else if (fiber.child !== null) {
2302
- primaryChild = fiber.child.child;
2303
- }
2304
- if (primaryChild !== null) {
2305
- mountFiberRecursively(
2306
- primaryChild,
2307
- newParentInstance,
2308
- true,
2309
- traceNearestHostComponentUpdate,
2310
- );
2311
- }
2282
+ // We intentionally do not re-enable the traceNearestHostComponentUpdate flag in this branch,
2283
+ // because we don't want to highlight every host node inside of a newly mounted subtree.
2284
+ }
2285
+
2286
+ if (fiber.tag === SuspenseComponent) {
2287
+ const isTimedOut = fiber.memoizedState !== null;
2288
+ if (isTimedOut) {
2289
+ // Special case: if Suspense mounts in a timed-out state,
2290
+ // get the fallback child from the inner fragment and mount
2291
+ // it as if it was our own child. Updates handle this too.
2292
+ const primaryChildFragment = fiber.child;
2293
+ const fallbackChildFragment = primaryChildFragment
2294
+ ? primaryChildFragment.sibling
2295
+ : null;
2296
+ const fallbackChild = fallbackChildFragment
2297
+ ? fallbackChildFragment.child
2298
+ : null;
2299
+ if (fallbackChild !== null) {
2300
+ mountChildrenRecursively(
2301
+ fallbackChild,
2302
+ newParentInstance,
2303
+ traceNearestHostComponentUpdate,
2304
+ );
2305
}
2306
} else {
2314
- if (fiber.child !== null) {
2315
- mountFiberRecursively(
2316
- fiber.child,
2307
+ let primaryChild: Fiber | null = null;
2308
+ const areSuspenseChildrenConditionallyWrapped =
2309
+ OffscreenComponent === -1;
2310
+ if (areSuspenseChildrenConditionallyWrapped) {
2311
+ primaryChild = fiber.child;
2312
+ } else if (fiber.child !== null) {
2313
+ primaryChild = fiber.child.child;
2314
+ }
2315
+ if (primaryChild !== null) {
2316
+ mountChildrenRecursively(
2317
+ primaryChild,
2318
newParentInstance,
2318
- true,
2319
traceNearestHostComponentUpdate,
2320
);
2321
}
2322
}
2323
-
2324
- // We're exiting this Fiber now, and entering its siblings.
2325
- // If we have selection to restore, we might need to re-activate tracking.
2326
- updateTrackedPathStateAfterMount(mightSiblingsBeOnTrackedPath);
2327
-
2328
- fiber = traverseSiblings ? fiber.sibling : null;
2323
+ } else {
2324
+ if (fiber.child !== null) {
2325
+ mountChildrenRecursively(
2326
+ fiber.child,
2327
+ newParentInstance,
2328
+ traceNearestHostComponentUpdate,
2329
+ );
2330
+ }
2331
}
2332
+
2333
+ // We're exiting this Fiber now, and entering its siblings.
2334
+ // If we have selection to restore, we might need to re-activate tracking.
2335
+ updateTrackedPathStateAfterMount(mightSiblingsBeOnTrackedPath);
2336
}
2337
2338
// We use this to simulate unmounting for Suspense trees
2339
// when we switch from primary to fallback.
2334
- function unmountFiberChildrenRecursively(fiber: Fiber) {
2340
+ function unmountFiberRecursively(fiber: Fiber) {
2341
if (__DEBUG__) {
2336
- debug('unmountFiberChildrenRecursively()', fiber, null);
2342
+ debug('unmountFiberRecursively()', fiber, null);
2343
}
2344
2345
// We might meet a nested Suspense on our way.
2358
child = fallbackChildFragment ? fallbackChildFragment.child : null;
2359
}
2360
2361
+ unmountChildrenRecursively(child);
2362
+ }
2363
+
2364
+ function unmountChildrenRecursively(firstChild: null | Fiber) {
2365
+ let child: null | Fiber = firstChild;
2366
while (child !== null) {
2367
// Record simulated unmounts children-first.
2368
// We skip nodes without return because those are real unmounts.
2369
if (child.return !== null) {
2359
- unmountFiberChildrenRecursively(child);
2370
+ unmountFiberRecursively(child);
2371
recordUnmount(child, true);
2372
}
2373
child = child.sibling;
2506
}
2507
}
2508
2509
+ // Returns whether closest unfiltered fiber parent needs to reset its child list.
2510
+ function updateChildrenRecursively(
2511
+ nextFirstChild: null | Fiber,
2512
+ prevFirstChild: null | Fiber,
2513
+ parentInstance: DevToolsInstance | null,
2514
+ traceNearestHostComponentUpdate: boolean,
2515
+ ): boolean {
2516
+ let shouldResetChildren = false;
2517
+ // If the first child is different, we need to traverse them.
2518
+ // Each next child will be either a new child (mount) or an alternate (update).
2519
+ let nextChild = nextFirstChild;
2520
+ let prevChildAtSameIndex = prevFirstChild;
2521
+ while (nextChild) {
2522
+ // We already know children will be referentially different because
2523
+ // they are either new mounts or alternates of previous children.
2524
+ // Schedule updates and mounts depending on whether alternates exist.
2525
+ // We don't track deletions here because they are reported separately.
2526
+ if (nextChild.alternate) {
2527
+ const prevChild = nextChild.alternate;
2528
+ if (
2529
+ updateFiberRecursively(
2530
+ nextChild,
2531
+ prevChild,
2532
+ parentInstance,
2533
+ traceNearestHostComponentUpdate,
2534
+ )
2535
+ ) {
2536
+ // If a nested tree child order changed but it can't handle its own
2537
+ // child order invalidation (e.g. because it's filtered out like host nodes),
2538
+ // propagate the need to reset child order upwards to this Fiber.
2539
+ shouldResetChildren = true;
2540
+ }
2541
+ // However we also keep track if the order of the children matches
2542
+ // the previous order. They are always different referentially, but
2543
+ // if the instances line up conceptually we'll want to know that.
2544
+ if (prevChild !== prevChildAtSameIndex) {
2545
+ shouldResetChildren = true;
2546
+ }
2547
+ } else {
2548
+ mountFiberRecursively(
2549
+ nextChild,
2550
+ parentInstance,
2551
+ traceNearestHostComponentUpdate,
2552
+ );
2553
+ shouldResetChildren = true;
2554
+ }
2555
+ // Try the next child.
2556
+ nextChild = nextChild.sibling;
2557
+ // Advance the pointer in the previous list so that we can
2558
+ // keep comparing if they line up.
2559
+ if (!shouldResetChildren && prevChildAtSameIndex !== null) {
2560
+ prevChildAtSameIndex = prevChildAtSameIndex.sibling;
2561
+ }
2562
+ }
2563
+ // If we have no more children, but used to, they don't line up.
2564
+ if (prevChildAtSameIndex !== null) {
2565
+ shouldResetChildren = true;
2566
+ }
2567
+ return shouldResetChildren;
2568
+ }
2569
+
2570
// Returns whether closest unfiltered fiber parent needs to reset its child list.
2571
function updateFiberRecursively(
2572
nextFiber: Fiber,
2650
: null;
2651
2652
if (prevFallbackChildSet == null && nextFallbackChildSet != null) {
2581
- mountFiberRecursively(
2653
+ mountChildrenRecursively(
2654
nextFallbackChildSet,
2655
newParentInstance,
2584
- true,
2656
traceNearestHostComponentUpdate,
2657
);
2658
2678
// 2. Mount primary set
2679
const nextPrimaryChildSet = nextFiber.child;
2680
if (nextPrimaryChildSet !== null) {
2610
- mountFiberRecursively(
2681
+ mountChildrenRecursively(
2682
nextPrimaryChildSet,
2683
newParentInstance,
2613
- true,
2684
traceNearestHostComponentUpdate,
2685
);
2686
}
2690
// 1. Hide primary set
2691
// This is not a real unmount, so it won't get reported by React.
2692
// We need to manually walk the previous tree and record unmounts.
2623
- unmountFiberChildrenRecursively(prevFiber);
2693
+ unmountFiberRecursively(prevFiber);
2694
// 2. Mount fallback set
2695
const nextFiberChild = nextFiber.child;
2696
const nextFallbackChildSet = nextFiberChild
2697
? nextFiberChild.sibling
2698
: null;
2699
if (nextFallbackChildSet != null) {
2630
- mountFiberRecursively(
2700
+ mountChildrenRecursively(
2701
nextFallbackChildSet,
2702
newParentInstance,
2633
- true,
2703
traceNearestHostComponentUpdate,
2704
);
2705
shouldResetChildren = true;
2708
// Common case: Primary -> Primary.
2709
// This is the same code path as for non-Suspense fibers.
2710
if (nextFiber.child !== prevFiber.child) {
2642
- // If the first child is different, we need to traverse them.
2643
- // Each next child will be either a new child (mount) or an alternate (update).
2644
- let nextChild = nextFiber.child;
2645
- let prevChildAtSameIndex = prevFiber.child;
2646
- while (nextChild) {
2647
- // We already know children will be referentially different because
2648
- // they are either new mounts or alternates of previous children.
2649
- // Schedule updates and mounts depending on whether alternates exist.
2650
- // We don't track deletions here because they are reported separately.
2651
- if (nextChild.alternate) {
2652
- const prevChild = nextChild.alternate;
2653
- if (
2654
- updateFiberRecursively(
2655
- nextChild,
2656
- prevChild,
2657
- newParentInstance,
2658
- traceNearestHostComponentUpdate,
2659
- )
2660
- ) {
2661
- // If a nested tree child order changed but it can't handle its own
2662
- // child order invalidation (e.g. because it's filtered out like host nodes),
2663
- // propagate the need to reset child order upwards to this Fiber.
2664
- shouldResetChildren = true;
2665
- }
2666
- // However we also keep track if the order of the children matches
2667
- // the previous order. They are always different referentially, but
2668
- // if the instances line up conceptually we'll want to know that.
2669
- if (prevChild !== prevChildAtSameIndex) {
2670
- shouldResetChildren = true;
2671
- }
2672
- } else {
2673
- mountFiberRecursively(
2674
- nextChild,
2675
- newParentInstance,
2676
- false,
2677
- traceNearestHostComponentUpdate,
2678
- );
2679
- shouldResetChildren = true;
2680
- }
2681
- // Try the next child.
2682
- nextChild = nextChild.sibling;
2683
- // Advance the pointer in the previous list so that we can
2684
- // keep comparing if they line up.
2685
- if (!shouldResetChildren && prevChildAtSameIndex !== null) {
2686
- prevChildAtSameIndex = prevChildAtSameIndex.sibling;
2687
- }
2688
- }
2689
- // If we have no more children, but used to, they don't line up.
2690
- if (prevChildAtSameIndex !== null) {
2711
+ if (
2712
+ updateChildrenRecursively(
2713
+ nextFiber.child,
2714
+ prevFiber.child,
2715
+ newParentInstance,
2716
+ traceNearestHostComponentUpdate,
2717
+ )
2718
+ ) {
2719
shouldResetChildren = true;
2720
}
2721
} else {
2827
};
2828
}
2829
2802
- mountFiberRecursively(root.current, null, false, false);
2830
+ mountFiberRecursively(root.current, null, false);
2831
flushPendingEvents(root);
2832
currentRootID = -1;
2833
});
2926
if (!wasMounted && isMounted) {
2927
// Mount a new root.
2928
setRootPseudoKey(currentRootID, current);
2901
- mountFiberRecursively(current, null, false, false);
2929
+ mountFiberRecursively(current, null, false);
2930
} else if (wasMounted && isMounted) {
2931
// Update an existing root.
2932
updateFiberRecursively(current, alternate, null, false);
2938
} else {
2939
// Mount a new root.
2940
setRootPseudoKey(currentRootID, current);
2913
- mountFiberRecursively(current, null, false, false);
2941
+ mountFiberRecursively(current, null, false);
2942
}
2943
2944
if (isProfiling && isProfilingSupported) {