Repository: facebook/react
File: packages/react-devtools-shared/src/backend/fiber/renderer.js (JavaScript)

Code before:
  function didStatefulHookChange(prev: HooksNode, next: HooksNode): boolean {
    // Detect the shape of useState() / useReducer() / useTransition() / useSyncExternalStore() / useActionState()
    const isStatefulHook =
      prev.isStateEditable === true ||
      prev.name === 'SyncExternalStore' ||
      prev.name === 'Transition' ||
      prev.name === 'ActionState' ||
      prev.name === 'FormState';

    // Compare the values to see if they changed
    if (isStatefulHook) {
      return prev.value !== next.value;
    }

    return false;
  }

  function flattenHooksTree(hooksTree: HooksTree): HooksTree {
    const flattened: HooksTree = [];
    for (let i = 0; i < hooksTree.length; i++) {
      const currentHook = hooksTree[i];
      // If the hook has subHooks, flatten them recursively
      if (currentHook.subHooks && currentHook.subHooks.length > 0) {
        flattened.push(...flattenHooksTree(currentHook.subHooks));
        continue;
      }
      // If the hook doesn't have subHooks, add it to the flattened list
      flattened.push(currentHook);
    }
    return flattened;
  }

  function getChangedHooksIndices(
    prevHooks: HooksTree | null,
    nextHooks: HooksTree | null,
  ): null | Array<number> {
    if (prevHooks == null || nextHooks == null) {
      return null;
    }

    const prevFlattened = flattenHooksTree(prevHooks);
    const nextFlattened = flattenHooksTree(nextHooks);

    const indices: Array<number> = [];

    for (let index = 0; index < prevFlattened.length; index++) {
      const prevHook = prevFlattened[index];
      const nextHook = nextFlattened[index];

      if (prevHook === null || nextHook === null) {
        continue;

Review comment (suggestion):
Is there a reason why this specific order is chosen?

This looks like subHooks wills have lower indexes in an array, when flattened. In the UI, the hook numbers (index + 1) usually reflect the tree structure. Basically `indexOf(parent) < indexOf(parent.child)` is always true.

Code after:
  function didStatefulHookChange(prev: HooksNode, next: HooksNode): boolean {
    // Detect the shape of useState() / useReducer() / useTransition() / useSyncExternalStore() / useActionState()
    const isStatefulHook =
      prev.isStateEditable === true ||
      prev.name === 'SyncExternalStore' ||
      prev.name === 'Transition' ||
      prev.name === 'ActionState' ||
      prev.name === 'FormState';

    // Compare the values to see if they changed
    if (isStatefulHook) {
      return prev.value !== next.value;
    }

    return false;
  }

  function getChangedHooksIndices(
    prevHooks: HooksTree | null,
    nextHooks: HooksTree | null,
  ): null | Array<number> {
    if (prevHooks == null || nextHooks == null) {
      return null;
    }

    const indices: Array<number> = [];
    let index = 0;

    function traverse(prevTree: HooksTree, nextTree: HooksTree): void {
      for (let i = 0; i < prevTree.length; i++) {
        const prevHook = prevTree[i];
        const nextHook = nextTree[i];

        if (prevHook.subHooks.length > 0 && nextHook.subHooks.length > 0) {
          traverse(prevHook.subHooks, nextHook.subHooks);
          continue;
        }

        if (didStatefulHookChange(prevHook, nextHook)) {
          indices.push(index);
        }

        index++;
      }
    }

    traverse(prevHooks, nextHooks);
    return indices;
  }

  function getChangedKeys(prev: any, next: any): null | Array<string> {