-
-
Notifications
You must be signed in to change notification settings - Fork 312
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
fix: prevent returning items when container size is zero #598
Conversation
Hey @piecyk , useEffect(() => {
if (data.length === 0) {
return;
}
const firstSelectedRow = Object.keys(cache.getSelectedRows())[0];
if (firstSelectedRow) {
rowVirtualizer.scrollToIndex(firstSelectedRow, {
align: "center",
smoothScroll: false,
});
}
}, [data, cache, rowVirtualizer]); The code is in my table component and ensures that the last selected row is visible for the user. This worked fine with Can you please help me? thanks |
@wuarmin to keep selected rows rendered, please use range extractor import { defaultRangeExtractor, useVirtualizer } from '@tanstack/react-virtual'
const Foo = () => {
const selectedIndexes = [0, 50]
const virtualizer = useVirtualizer({
count: 100,
getScrollElement: () => ref.current,
estimateSize: () => 50,
rangeExtractor: React.useCallback((range: Range) => {
if (selectedIndexes.length === 0) {
return defaultRangeExtractor(range)
} else {
const next = new Set([...selectedIndexes, ...defaultRangeExtractor(range)])
return [...next].sort((a, b) => a - b)
}
}, [selectedIndexes]),
})
return ...
} |
@piecyk Thanks, I have tried this, but the behavior is not as expected. I just want to keep the scrollbar state when user switch between pages. The table should be scrolled so that the selected row is visible. The useEffect code did this flawlessly. Why does this no longer work? Thanks |
@wuarmin ooo that is not good, could you add an issue and create codesandbox example? This need to be fixed before v3 release. |
@wuarmin I think i see the issue, for now you can wrap the scrollToIndex with requestAnimationFrame requestAnimationFrame(() => {
virtualizer.scrollToIndex(100)
}) |
@piecyk Thank you very much! That works. Could you please briefly explain, why Thanks |
It's for couple of reasons, mainly the scroll to index when we have dynamic case is not trivial. When jumping to index after items are mounted we need to read actual size compare it with estimated and update the list if needed, then the position of the index will change, long story short on lib side we do it in loop ( pushing to next tick, so the browser will have time to render items and we can read the size ) it's enabled when we detect dynamic mode. Basic changes showed that the how we detect dynamic mode is not to robust ( we check if we have the elements in cache )
to push the scrolling to next tick, when we have the elements rendered. |
I have this working on my application, I just had to add the ?? 0 to the But this change made testing with react-testing-library much more difficult because it doesn't actually render in a browser. The actual size of the containers is 0. If you have any suggestions @piecyk please let me know. Options like using cypress are not available to me. I'll try a few things, but open to suggestions. It would be like similar to writing a unit test for the https://tanstack.com/virtual/v3/docs/examples/react/dynamic list example. |
@FBNitro that is true, for unit test you can mock the getBoundingClientRect for the scroller element, or provide observeElementRect when testing. |
Ah great, thank you for guiding me in the correct direction. I updated the getBoundingClientRect in the htmlElement prototype and that worked for me. const DEFAULT_BOUNDING_CLIENT_RECT = window.HTMLElement.prototype.getBoundingClientRect;
beforeAll(() => {
window.HTMLElement.prototype.getBoundingClientRect = () =>
({
height: 800,
width: 500
}) as DOMRect;
});
afterAll(() => {
window.HTMLElement.prototype.getBoundingClientRect = DEFAULT_BOUNDING_CLIENT_RECT;
}); |
Shouldn't rangeExtractor({
...range,
overscan,
count: outerSize === 0 ? 0 : count,
}) |
don't think so, wouldn't it be counterintuitive that virtualizer returns items here |
I'm probably wrong, but I always thought of overscan as some sort of guarantee that at least that much rows will be rendered/returned. The docs define overscan as "The number of items to render above and below the visible area.' Here if the container size is 0, the visible area will be 0. Technically 0 + say 10 overscan should still be 10. But yeah I guess this is a bit up to how one is interpreting things. |
Yeah, for example, do we still have visible area if it's 0, also if virtualizer returns range of type |
Ah, yeah. I guess this is fine. I'll create a separate issue if something goes south with this 👍 |
closes #596