@samitouri / QOS-React / commits / aa8469fefb

[DevTools] Rename mountFiberRecursively/updateFiberRecursively (#30586)

This is just for clarity at first. Before: - mountFiberRecursively accepts a set of children and flag that says whether to just do one - updateFiberRecursively accepts a fiber and loops over its children - unmountFiberChildrenRecursively accepts a fiber and loops over its children After: - mountFiberRecursively accepts a Fiber and calls mountChildrenRecursively - updateFiberRecursively accepts a Fiber and calls updateChildrenRecursively - unmountFiberRecursively accepts a Fiber and calls unmountChildrenRecursively - mountChildrenRecursively accepts a set of children and loops over each one - updateChildrenRecursively accepts a set of children and loops over each one - unmountChildrenRecursively accepts a set of children and loops over each one So now there's one place where things happens for the single item and one place where we do the loop.

Sebastian Markbåge committed Aug 2, 2024 at 17:53 UTC aa8469fefbde17ab9d2e2cc06deb0a8f153bd005
1 file changed +170 -142
packages/react-devtools-shared/src/backend/fiber/renderer.js
+170 -142
@@ -1094,7 +1094,7 @@ export function attach(
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,112 +2228,118 @@ export function attach(
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.
@@ -2352,11 +2358,16 @@ export function attach(
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;
@@ -2495,6 +2506,67 @@ export function attach(
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,
@@ -2578,10 +2650,9 @@ export function attach(
2650 : null;
2651
2652 if (prevFallbackChildSet == null && nextFallbackChildSet != null) {
2581 - mountFiberRecursively(
2653 + mountChildrenRecursively(
2654 nextFallbackChildSet,
2655 newParentInstance,
2584 - true,
2656 traceNearestHostComponentUpdate,
2657 );
2658
@@ -2607,10 +2678,9 @@ export function attach(
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 }
@@ -2620,17 +2690,16 @@ export function attach(
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;
@@ -2639,55 +2708,14 @@ export function attach(
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 {
@@ -2799,7 +2827,7 @@ export function attach(
2827 };
2828 }
2829
2802 - mountFiberRecursively(root.current, null, false, false);
2830 + mountFiberRecursively(root.current, null, false);
2831 flushPendingEvents(root);
2832 currentRootID = -1;
2833 });
@@ -2898,7 +2926,7 @@ export function attach(
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);
@@ -2910,7 +2938,7 @@ export function attach(
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) {