Repository: facebook/react
File: packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseTimeline.js (JavaScript)

Code before:
      // You should be able to see what suspended the shell.
      if (!uniqueSuspendersOnly || current.hasUniqueSuspenders) {
        list.push(current);
      }
      // Add children in reverse order to maintain document order
      for (let j = current.children.length - 1; j >= 0; j--) {
        const childSuspense = store.getSuspenseByID(current.children[j]);
        if (childSuspense !== null) {
          stack.push(childSuspense);
        }
      }
    }
  }

  return list;
}

function SuspenseTimelineInput({rootID}: {rootID: Element['id'] | void}) {
  const bridge = useContext(BridgeContext);
  const store = useSuspenseStore();
  const dispatch = useContext(TreeDispatcherContext);
  const {highlightHostInstance, clearHighlightHostInstance} =
    useHighlightHostInstance();
  const [uniqueSuspendersOnly, setUniqueSuspendersOnly] = useState(true);
  function handleToggleUniqueSuspenders(event: SyntheticEvent) {
    // TODO: Currently loses timeline position because position is timeline-index-based not suspense-id-based.
    setUniqueSuspendersOnly((event.currentTarget as HTMLInputElement).checked);
  }

  const timeline = useMemo(() => {
    return getSuspendableDocumentOrderSuspense(
      store,
      rootID,
      uniqueSuspendersOnly,
    );
  }, [store, rootID, uniqueSuspendersOnly]);

  const inputRef = useRef<HTMLElement | null>(null);
  const inputBBox = useRef<ClientRect | null>(null);
  useLayoutEffect(() => {
    if (timeline.length === 0) {
      return;
    }

    const input = inputRef.current;
    if (input === null) {
      throw new Error('Expected an input HTML element to be present.');
    }

    inputBBox.current = input.getBoundingClientRect();
    const observer = new ResizeObserver(entries => {

Review comment (bug):
Requires a larger refactor. The timeline should just live in state like the Components tree related things. I noticed some more bugs with the backend reconciler though so I'll focus on that. 

Code after:
  }

  const inputRef = useRef<HTMLElement | null>(null);
  const inputBBox = useRef<ClientRect | null>(null);
  useLayoutEffect(() => {
    if (timeline.length === 0) {
      return;
    }

    const input = inputRef.current;
    if (input === null) {
      throw new Error('Expected an input HTML element to be present.');
    }

    inputBBox.current = input.getBoundingClientRect();
    const observer = new ResizeObserver(entries => {
      inputBBox.current = input.getBoundingClientRect();
    });
    observer.observe(input);
    return () => {
      inputBBox.current = null;
      observer.disconnect();
    };
  }, [timeline.length]);

  const min = 0;
  const max = timeline.length > 0 ? timeline.length - 1 : 0;

  if (rootID === null) {
    return (
      <div className={styles.SuspenseTimelineInput}>No root selected.</div>
    );
  }

  if (!store.supportsTogglingSuspense(rootID)) {
    return (
      <div className={styles.SuspenseTimelineInput}>
        Can't step through Suspense in production apps.
      </div>
    );
  }

  if (timeline.length === 0) {
    return (
      <div className={styles.SuspenseTimelineInput}>
        Root contains no Suspense nodes.
      </div>
    );
  }

  function switchSuspenseNode(nextTimelineIndex: number) {