headless-tree icon indicating copy to clipboard operation
headless-tree copied to clipboard

Focus to next item breaks with dynamic item height

Open frontend-sensei opened this issue 7 months ago • 5 comments
trafficstars

Describe the bug Focus to next item via keyboard breaks, when virtualizer measure item height dynamically.

To Reproduce Focus on the tree item by mouse click. Navigate to the bottom by pressing arrow down until it stop on some item. If you will click by mouse again on any visible item, navigation start works but after the same navigation to the bottom it will break again.

CodeSandbox

Expected behavior Navigation works well =)

Additional context

  1. I took BasicVirtualization example from HeadlessTree which works well.
  2. I look into example of rendering dynamic item height from react-virtual
  3. I've added this to rendering item. It's key, ref and data-index.
{virtualizer.getVirtualItems().map((virtualItem) => {
    const item = tree.getItems()[virtualItem.index];
    return (
      <button
        {...item.getProps()}
        key={virtualItem.key}
        data-index={virtualItem.index}
        ref={virtualizer.measureElement}
  1. I've tried to debug and found that in the hotkeys-feature implementation, in the onTreeMount method, keydown listener stops firing by some reason. But I can't understand why exactly.

I'm talking about this part of your code:

// keyup is registered on document, because some hotkeys shift
// the focus away from the tree (i.e. search)
// and then we wouldn't get the keyup event nevermore
element.addEventListener('keydown', keydown)

frontend-sensei avatar Apr 21 '25 12:04 frontend-sensei

Hi, thanks for the report! I can't seem to access the codesandbox, and get a "Devbox not found" error. Can you please see if that sandbox has public access enabled?

lukasbach avatar Apr 21 '25 15:04 lukasbach

Yes, I see, sorry. Now is public

frontend-sensei avatar Apr 21 '25 15:04 frontend-sensei

Hi! Maybe you can suggest me what to debug to help you fix that?

frontend-sensei avatar Apr 24 '25 08:04 frontend-sensei

Hi, sorry for remaining silent on this. I saw the sandbox and can reproduce the issue, but haven't had time to dig deeper, I'll update you soon on this again.

lukasbach avatar Apr 24 '25 17:04 lukasbach

I guess the cause for the issue is, that by supplying a custom ref to your tree element, you override the ref supplied by the treeItem.getProps(), so some functionality gets lost. It seems to work with this:

const props = item.getProps();
return (
  <button
    {...props}
    key={virtualItem.key}
    data-index={virtualItem.index}
    ref={(r) => {
      virtualizer.measureElement(r);
      props.ref(r);
    }}

For some reason, calling item.getProps() twice still causes a different issue, so you need to just call it once if you want to use props and the produced ref seperately, I'll look into that as well, but hopefully this is enough for you to get unblocked by this.

lukasbach avatar May 01 '25 13:05 lukasbach

Ohh, I see. This solution helped me. Thank you very much!

frontend-sensei avatar May 05 '25 11:05 frontend-sensei

Happy that this was helpful! I found that the follow up bug I mentioned, was not from calling item.getProps() twice, but rather that item.getProps() may not be called inside a ref callback, since the ref callback will also trigger on unmount and at that time, the item may already not be registered anymore with HT. It works fine otherwise though. I've added a remark on this in the docs on virtualization, and would close this if this fixes your problem.

lukasbach avatar May 06 '25 22:05 lukasbach

Yes, Thank you!

frontend-sensei avatar May 07 '25 06:05 frontend-sensei