Replies: 7 comments 37 replies
-
The only thing that worked for us so far is a double As you've noticed both prepending or appending an item would change scroll height, but prepending pushes visible content while appending does not The
It's relatively easy to try it out, but it has some potential drawbacks
So here it is:
Pushing more items would add them to the top but would not cause scroll/content issues Since your case is a chat - I'm not sure whether there won't be the same problem when new messages arrive and they need to be added to the bottom. It might be possible to just scroll to index 0 (bottom) when new messages arrive (if you were already on the bottom) For our case the scaleY hack works BTW you also have to invert the mouse useEffect(() => {
const el = parentRef.current;
const invertedWheelScroll = (event) => {
el.scrollTop -= event.deltaY;
event.preventDefault();
};
el.addEventListener('wheel', invertedWheelScroll, false);
return () => el.removeEventListener('wheel', invertedWheelScroll);
}, []); |
Beta Was this translation helpful? Give feedback.
-
Man, this gets hairy- you gotta
EDIT/UPDATE I think you can actually do 4. with the I have a loosely passable POC but it's not in a good state to share at the moment. happy to discuss + send snippets if there is still interest. This blog post was helpful for me |
Beta Was this translation helpful? Give feedback.
-
I think we can go with this https://codesandbox.io/s/beautiful-meninsky-fr6csu?file=/pages/index.js The trick is to update scroll offset in same time when new items are prepend, and reverse indexes. |
Beta Was this translation helpful? Give feedback.
-
Hello, I ended up in this issue a couple of days ago searching for an efficient way to have two-direction infinite loader+scrolling with virtual and react-table. Your answers above helped a lot and I ended up with an approach that I believe works well enough. (Still wip - especially from the perf aspect) I work at netdata and you can check the end result (without signup or anything) here. 480p.movI used both react-table and react-virtual. Now for the problem at hand, bi-directional infinite scrolling, I used the usual implementation for infinite loading with the difference of adding a second The gist of what I used is this:
So in our use case:
The same happens when the user keeps scrolling up and fetches more and more data. All of the above are happening with just a small flickering the minute before Hope this helps anyone who wants to try something similar. |
Beta Was this translation helpful? Give feedback.
-
Did somebody have success reversing the arrow and |
Beta Was this translation helpful? Give feedback.
-
if anyone is interested, I have managed to create a reversed chat-like layout with https://stackblitz.com/edit/vitejs-vite-e28dau?file=src%2FVirtualList.tsx Component for referenceimport React, { CSSProperties, useCallback, useEffect, useRef } from 'react';
import { VirtualItem, useVirtualizer } from '@tanstack/react-virtual';
export type VirtualListProps<T> = {
className?: string;
style?: CSSProperties;
itemClassName?: string;
itemStyle?: CSSProperties;
items: T[];
getItemKey: (item: T, index: number) => string | number;
renderItem: (item: T, virtualItem: VirtualItem) => React.ReactNode;
estimateSize: (index: number) => number;
overscan?: number;
};
export function VirtualList<T>({
style,
itemStyle,
items,
getItemKey,
estimateSize,
renderItem,
overscan,
}: VirtualListProps<T>) {
const scrollableRef = useRef<HTMLDivElement>(null);
const getItemKeyCallback = useCallback(
(index: number) => getItemKey(items[index]!, index),
[getItemKey, items]
);
const virtualizer = useVirtualizer({
count: items.length,
getItemKey: getItemKeyCallback,
getScrollElement: () => scrollableRef.current,
estimateSize,
overscan,
debug: true,
});
useEffect(
function scrollToEnd() {
virtualizer.scrollToIndex(items.length - 1);
},
[items]
);
const virtualItems = virtualizer.getVirtualItems();
return (
<div
style={{
display: 'flex',
flexDirection: 'column-reverse',
...style,
}}
>
<div
ref={scrollableRef}
style={{
overflow: 'auto',
}}
>
<div
style={{
width: '100%',
position: 'relative',
height: virtualizer.getTotalSize(),
}}
>
<div
style={{
position: 'absolute',
top: '0',
left: '0',
width: '100%',
transform: `translateY(${virtualItems[0]?.start ?? 0}px)`,
}}
>
{virtualItems.map((virtualItem) => {
const item = items[virtualItem.index]!;
return (
<div
key={virtualItem.key}
ref={virtualizer.measureElement}
data-index={virtualItem.index}
style={itemStyle}
>
{renderItem(item, virtualItem)}
</div>
);
})}
</div>
</div>
</div>
</div>
);
} |
Beta Was this translation helpful? Give feedback.
-
I just solved it by making sure estimateSize is always larger than the list elements. const virtualizer = useVirtualizer({
estimateSize: () => 999,
// ...
});
// ...
useEffect(
() => {
virtualizer.scrollToIndex(items.length - 1);
},
[items]
); |
Beta Was this translation helpful? Give feedback.
-
We have been using react-virtual for a few months, and love the fact that the library is headless and allow us to do things our way... However, we are trying to build a chat-like virtual list that starts at the bottom, we then prepend elements to the list when we reach the top. Nothing too fancy except that the messages have fairly variable sizes.
The problems started to show up when we started experimenting with bigger messages... It seems that if we used a list that would not start from the bottom we would not have nearly as many issues... Mostly because the height of the list gets calculated and as you usually scroll down, the list height expands but you are left at the same scroll position (so nothing more than a little flicker in the scrollbar). However when the list is inverted and the elements above your overscan are rendered, the height of the elements pushes the elements that are in view downward (happens only when the estimate size is not 100% correct).
Furthermore, we have a functionality to scroll to the bottom of the list which we achieve by scrolling to a big offset. The reason for that is due to the padding at the bottom, scrollToIndex would not work for us. However since our estimate function is not 100% accurate the scroll happens as follow:
It seems that this is something inevitable, and that is why we have a double scroll in the scrollToIndex.
So I was wondering if anyone has ever done a reversed list with dynamic elements and could give some guidance, or create an example.
Beta Was this translation helpful? Give feedback.
All reactions