UNPKG

@supunlakmal/hooks

Version:

A collection of reusable React hooks

61 lines 2.76 kB
import { useState, useEffect, useRef, useCallback } from 'react'; /** * Optimizes rendering of long lists by only rendering the items currently visible * in the viewport, plus a configurable number of items above and below (overscan). * Assumes fixed item height for simplicity. * * @template T The type of the items in the list. * @param options Configuration options including the list data, item height, and container ref. * @returns An object containing the items to render and the total list height. */ export const useVirtualList = (options) => { var _a; const { list, itemHeight, containerRef, overscan = 5, initialScrollTop = 0, onScroll, } = options; const [scrollTop, setScrollTop] = useState(initialScrollTop); const innerRef = useRef(null); // Update scroll position on scroll events const handleScroll = useCallback((event) => { const currentScrollTop = event.target.scrollTop; setScrollTop(currentScrollTop); onScroll === null || onScroll === void 0 ? void 0 : onScroll(currentScrollTop); }, [onScroll]); useEffect(() => { const container = containerRef.current; if (!container) return; // Set initial scroll position if provided if (initialScrollTop > 0) { container.scrollTop = initialScrollTop; } container.addEventListener('scroll', handleScroll); return () => container.removeEventListener('scroll', handleScroll); }, [containerRef, handleScroll, initialScrollTop]); const totalHeight = list.length * itemHeight; // Calculate visible items based on scroll position const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan); const endIndex = Math.min(list.length - 1, Math.floor((scrollTop + (((_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.clientHeight) || 0)) / itemHeight) + overscan); const virtualItems = list .slice(startIndex, endIndex + 1) .map((item, index) => { const originalIndex = startIndex + index; return { data: item, index: originalIndex, offsetTop: originalIndex * itemHeight, }; }); // Style the inner container to position items correctly useEffect(() => { if (innerRef.current) { // Although items are positioned absolutely, setting the height ensures scrollbar size is correct. innerRef.current.style.height = `${totalHeight}px`; innerRef.current.style.position = 'relative'; // Needed for absolute positioning of children } }, [totalHeight]); return { virtualItems, totalHeight, innerRef, }; }; //# sourceMappingURL=useVirtualList.js.map