useWindowVirtualizer shows blank when having other contents on page
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 have you got the solution?
@ajcodeskills a hack of it is to increase the overscan number, eg. put 30 in my example.
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.)
I have this problem too. But I can't solve it yet.
And have to increase the overscan like @casprwang.
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?
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.
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
Unfortunately my solution does not seem to work when using initialOffset, scrollToOffset, scrollToIndex, etc
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.
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.
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
initialOffsetspecified.
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 :)