Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

useWindowVirtualizer with measureElement, hidden list, and auto-loading items causes getVirtualItems length to go to infinity #697

Open
2 tasks done
ConnorLanglois opened this issue Apr 2, 2024 · 1 comment

Comments

@ConnorLanglois
Copy link

ConnorLanglois commented Apr 2, 2024

Describe the bug

I am using useWindowVirtualizer along with auto-loading new items once the user reaches the bottom of the page. I do this by comparing if virtualItems[virtualItems.length - 1].index >= nItems - 2 (as you will see in the codesandbox). This works well. However, once I add

ref={virtualizer.measureElement}
data-index={item.index}

and also hide the list div by using display: none, the length of getVirtualItems() for some reason always increases to the total number of items, i.e. count. This causes my auto-load function to load the next items, which then increases the count, which then causes getVirtualItems() length to increase again, causing the auto-load function to run, and so on forever.

It appears that the use of display: none along with measureElement breaks something and causes getVirtualItems() to return the entire (or close to) list of elements in memory, i.e. count.

You will also notice in the codesandbox and video below that after re-showing the list after hiding it, it looks like that when the window is scrolled to the top the length of virtual items skyrockets and stacks most of the items on top of each other in z-axis.

I am not sure if this bug occurs with a non-window virtualizer.

Your minimal, reproducible example

https://codesandbox.io/p/github/ConnorLanglois/tanstack-virtual-window-infinite-bug

Steps to reproduce

  1. Go to the codesandbox.
  2. Notice the top-right stats box showing current virtual items count. Everything is fine here and scrolling loads more items.
  3. Click the button to hide the list.
  4. Notice how now the numbers are increasing forever, which in a real setting causes infinite api calls to backend and slows down browser

Expected behavior

Expected that nothing should change when hiding the list, or at least nothing as drastic as this

How often does this bug happen?

Every time

Screenshots or Videos

tanstack-virtual-window-infinite-bug.mp4

Platform

macOS
Chrome 123.0.6312.87

tanstack-virtual version

3.2.0

TypeScript version

5.2.2

Additional context

The example uses vite since it's a fork of the example from this repo, but the issue also occurs when using nextjs too.

Terms & Code of Conduct

  • I agree to follow this project's Code of Conduct
  • I understand that if my bug cannot be reliable reproduced in a debuggable environment, it will probably not be fixed and this issue may even be closed.
@ConnorLanglois ConnorLanglois changed the title useWindowVirtualizer with measureElement and auto-load items causes getVirtualItems length to go to infinity useWindowVirtualizer with measureElement, hidden list, and auto-load items causes getVirtualItems length to go to infinity Apr 2, 2024
@ConnorLanglois ConnorLanglois changed the title useWindowVirtualizer with measureElement, hidden list, and auto-load items causes getVirtualItems length to go to infinity useWindowVirtualizer with measureElement, hidden list, and auto-loading items causes getVirtualItems length to go to infinity Apr 2, 2024
@piecyk
Copy link
Collaborator

piecyk commented Jun 21, 2024

@ConnorLanglois the issue here is that when hiding a list with display none resize observer will kiks in and read the size of elements as 0. There are two solution for it, you can pass custom measureElement that will return prev size when updateSize size false,

export const useVirtual = <TScrollElement extends Element, TItemElement extends HTMLElement>({
  updateSize = true,
  virtualizerRef,
  ...options
}: VirtualizerOptions<TScrollElement, TItemElement>) => {
  const virtualizer = useVirtualizer({
    ...options,
    measureElement: (
      element: TItemElement,
      entry: ResizeObserverEntry | undefined,
      instance: Virtualizer<TScrollElement, TItemElement>,
    ) => {
      if (options.measureElement) {
        return options.measureElement(element, entry, instance)
      }
      const getSize = () => measureElement(element, entry, instance)

      if (updateSize) {
        return getSize()
      } else {
        const item = instance.measurementsCache[instance.indexFromElement(element)]

        return item ? item.size : getSize()
      }
    },
  })

Or use the new enabled option, but that will reset the whole state. Maybe we should have some more convenient way to disable RO in cases like this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants