useVirtualization.ts•1.32 kB
import { useState, useMemo, useCallback } from 'react';
interface VirtualizationOptions {
itemHeight: number;
containerHeight: number;
overscan?: number;
}
export const useVirtualization = <T>(
items: T[],
options: VirtualizationOptions
) => {
const [scrollTop, setScrollTop] = useState(0);
const { itemHeight, containerHeight, overscan = 5 } = options;
const visibleRange = useMemo(() => {
const visibleCount = Math.ceil(containerHeight / itemHeight);
const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
const endIndex = Math.min(items.length - 1, startIndex + visibleCount + overscan * 2);
return { startIndex, endIndex, visibleCount };
}, [scrollTop, itemHeight, containerHeight, overscan, items.length]);
const visibleItems = useMemo(() => {
return items.slice(visibleRange.startIndex, visibleRange.endIndex + 1);
}, [items, visibleRange.startIndex, visibleRange.endIndex]);
const totalHeight = items.length * itemHeight;
const offsetY = visibleRange.startIndex * itemHeight;
const handleScroll = useCallback((event: React.UIEvent<HTMLDivElement>) => {
setScrollTop(event.currentTarget.scrollTop);
}, []);
return {
visibleItems,
totalHeight,
offsetY,
handleScroll,
startIndex: visibleRange.startIndex
};
};