virtual icon indicating copy to clipboard operation
virtual copied to clipboard

useWindowVirtualizer shows blank when having other contents on page

Open casprwang opened this issue 3 years ago • 4 comments

Describe the bug

Hello, thanks for open sourcing this great library.

One potential bug: when using useWindowVirtualizer, I noticed the virtualized content would start showing blank items when scrolling down, if the page has some other normal contents on top.

Your minimal, reproducible example

https://codesandbox.io/s/friendly-noether-6f2p8z?file=/src/App.tsx

Steps to reproduce

scroll down on the sandbox demo

Expected behavior

the virtualized content should not blank on the visible window

How often does this bug happen?

No response

Screenshots or Videos

No response

Platform

MacOS

tanstack-virtual version

v3.0.0-beta.18

TypeScript version

No response

Additional context

No response

Terms & Code of Conduct

  • [X] I agree to follow this project's Code of Conduct
  • [X] 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.

casprwang avatar Sep 12 '22 06:09 casprwang

@casprwang have you got the solution?

ajcodeskills avatar Sep 15 '22 06:09 ajcodeskills

@ajcodeskills a hack of it is to increase the overscan number, eg. put 30 in my example.

casprwang avatar Sep 15 '22 22:09 casprwang

I've discovered that the issue isn't just for useWindowVirtualier, it applies for useVirtualizer as well. Increasing the overscan can be a workaround. I'd be happy to help debugging if there's some sort of contribution guidance. (I clone the repo but couldn't figure out how to develop.)

casprwang avatar Sep 15 '22 22:09 casprwang

I have this problem too. But I can't solve it yet. And have to increase the overscan like @casprwang.

meotimdihia avatar Sep 16 '22 05:09 meotimdihia

I was having this issue on useVirtualizer as well and realised getScrollElement would return null on the first render, my container element on the next, and null again after that.

I changed my code a bit and made sure getScrollElement would always return my container element and that worked for me.

In my case the containerRef was passed from a custom Context so here's what I changed:

// From
{
    const { containerRef } = useContext(VirtualListContext) // my custom context
    const virtualizer = useVirtualizer({
        getScrollElement: () => {
            console.log(containerRef.current) // null, my container el, null again (explaining the blank when scrolling)
            return containerRef.current
        },
        ...
    })

    useDidMount(() => {
        virtualizer._willUpdate()
    })
}

// To
{
    const { containerRef } = useContext(VirtualListContext) // my custom context
    const containerRefEl = useMemo(() => containerRef.current, [containerRef.current])
    const virtualizer = useVirtualizer({
        getScrollElement: () => {
            console.log(containerRefEl) // null then my container el
            return containerRefEl || containerRef.current
        },
        ...
    })

    useDidMount(() => {
        virtualizer._willUpdate()
    })
}

I understand that this won't fix the useWindowVirtualizer since there's no need for getScrollElement but this might hint that the issue comes from a similar reference issue regarding the list container?

Wurielle avatar Sep 29 '22 08:09 Wurielle

I'm running into the same issue and I thought that's what paddingStart and/or scrollPaddingStart were for but either I'm using them wrong or that's not what they're for!

For now, it seems like adding overscan is the only way to get around this issue.

Zertz avatar Sep 29 '22 15:09 Zertz

Hey guys, I think I found a pretty good solution here:

observeElementOffset: (instance, cb) =>
  observeWindowOffset(instance, (offset) => cb(offset - heightOfStuffAboveList));

https://codesandbox.io/s/reverent-jackson-spo5iu?file=/src/App.tsx

You still have to know (or measure) the offset of your virtual list from the top of the page, but you don't have to render any extra items through overscan. And it can be pretty easy to get the offset of an element from the top of the page: https://stackoverflow.com/a/34422437/10015870

dylanwulf avatar Sep 29 '22 16:09 dylanwulf

Unfortunately my solution does not seem to work when using initialOffset, scrollToOffset, scrollToIndex, etc

dylanwulf avatar Sep 29 '22 22:09 dylanwulf

I was able to fix the scrolling methods by providing a custom scrollToFn:

scrollToFn: (offset, canSmooth, instance) => {
  window.scrollTo({
    top:
      offset +
      window.scrollY +
      (virtualListRef.current?.getBoundingClientRect().top ?? 0),
    behavior: canSmooth ? "smooth" : undefined
  });

but now my problem is that it automatically scrolls to the first element of my list on mount, even if I don't have initialOffset specified.

dylanwulf avatar Sep 29 '22 23:09 dylanwulf

Yeah, here we need to add something like scrollMargin that will handle this cases. @dylanwulf was on right path, internally we need to skip altering scrollToFn when syncing scroll position.

piecyk avatar Oct 15 '22 13:10 piecyk

I was able to fix the scrolling methods by providing a custom scrollToFn:

scrollToFn: (offset, canSmooth, instance) => {
  window.scrollTo({
    top:
      offset +
      window.scrollY +
      (virtualListRef.current?.getBoundingClientRect().top ?? 0),
    behavior: canSmooth ? "smooth" : undefined
  });

but now my problem is that it automatically scrolls to the first element of my list on mount, even if I don't have initialOffset specified.

I my case I'm using a Switch component to render or not the grid with useWindowVirtualizer and the window scroll was moving to the top always... so I put a simple code to avoid this behavior:

  const virtualizer = useWindowVirtualizer({
    scrollToFn: () => {
      const scrollPosition = window.scrollY || document.documentElement.scrollTop
      window.scrollTo({ top: scrollPosition, behavior: 'auto' })
    },
  })

Thanks :)

mateusdotcc avatar Dec 08 '23 13:12 mateusdotcc