[Question/Feature Request] Is there a way to identify items that are rendered just for overscan? #294
Replies: 3 comments
-
You need to know when an element is in the viewport? React Virtual might be able to solve this, but it seems like this problem is more in the domain of an IntersectionObserver. Here's some sample code adapted from one of my projects that you might find helpful. Modify it as you see fit. import React from 'react';
import {useVirtual} from 'react-virtual';
export function ItemList({items}: {items: {id: string, name: string}[]) {
const mainRef = React.useRef<HTMLDivElement>(null!);
const {scrollToIndex, ...virtualizer} = useVirtual({
size: items.length,
parentRef: mainRef,
estimateSize: React.useCallback(() => 500, []),
overscan: 1,
horizontal: true,
});
const [activeItem, setActiveItem] = React.useState<string | null>(null);
// Update the current activeItem when scrolling occurs
const observer = React.useRef<IntersectionObserver | null>(null);
React.useEffect(() => {
// Keep a list of all intersection ratios.
const ratios: {[id: string]: number} = {};
// Then whenever a threshold is crossed...
observer.current = new IntersectionObserver(
entries => {
// update the above list,
entries.forEach(entry => {
const id = entry.target.getAttribute('data-id')!;
ratios[id] = entry.intersectionRatio;
});
// find the first page that has the highest intersection ratio,
const activePage = Object.entries(ratios)
.map(([id, ratio]) => ({id, ratio}))
.reduce((prev, cur) => (cur.ratio > prev.ratio ? cur : prev));
// and set it as the current page.
if (activePage) setActiveItem(activePage.id);
},
{threshold: [0, 0.5, 1], root: mainRef.current}
);
return () => observer.current!.disconnect();
}, []);
const observeElement = React.useCallback(element => {
if (observer.current && element) observer.current.observe(element);
}, []);
return (
<div ref={mainRef}>
<div
style={{
width: `${virtualizer.totalSize}px`,
position: 'relative',
height: "100%",
}}
>
{virtualizer.virtualItems.map(virtualColumn => {
const item = items[virtualColumn.index];
return (
<div
key={item.id}
data-id={item.id}
ref={observeElement}
style={{
position: 'absolute',
top: 0,
left: 0,
height: '100%',
width: virtualColumn.size,
transform: `translateX(${virtualColumn.start}px)`,
}}
>
<h2>{item.name}</h2>
<p>Active item: {activeItem}</p>
</div>
);
})}
</div>
</div>
);
} |
Beta Was this translation helpful? Give feedback.
-
useVirtual has an internal scrollOffset that is updated on each render. If this was exposed (#266) you could calculate what items are overscan based on the scrollOffset and the start/end of each item. |
Beta Was this translation helpful? Give feedback.
-
Yes, you can use the const visibleRangeRef = React.useRef([0, 0]);
const rowVirtualizer = useVirtual({
size: rows.length,
parentRef,
estimateSize: React.useCallback(() => 50, []),
rangeExtractor: React.useCallback(range => {
visibleRangeRef.current = [range.start, range.end]
return defaultRangeExtractor(range)
}, [])
}); |
Beta Was this translation helpful? Give feedback.
-
I have a scenario where I need to know visible items vs overscan items. I cannot simply count based on the overscan I passed since I might be in the beginning/end of the list so, 0 / length -1 would still be visible...
Is there a way to add a prop/class to items rendered just for overscan?
Beta Was this translation helpful? Give feedback.
All reactions