// @flow strict import * as React from 'react'; type InfiniteScrollOptions = { itemSize: number | ((index: number) => number), threshold: number, itemsLength: number, hasNextPage: boolean, loadMoreItems: () => Promise, containerHeight: number, isVariableSizeList?: boolean, }; export function useInfiniteScroll({ itemSize, threshold, itemsLength, hasNextPage, loadMoreItems, containerHeight, isVariableSizeList, }: InfiniteScrollOptions): (scrollData: { scrollOffset: number, scrollDirection: 'forward' | 'backward', }) => void { const [isLoading, setIsLoading] = React.useState(false); const loadingRef = React.useRef(false); const handleLoadMore = React.useCallback(async () => { if (!hasNextPage || isLoading || loadingRef.current) { return; } loadingRef.current = true; setIsLoading(true); try { await loadMoreItems(); } catch (err) { console.error('Error loading more items:', err); } finally { loadingRef.current = false; setIsLoading(false); } }, [hasNextPage, isLoading, loadMoreItems]); return React.useCallback( ({scrollOffset, scrollDirection}) => { if (scrollDirection !== 'forward') { return; } let totalSize = 0; if (isVariableSizeList && typeof itemSize === 'function') { for (let i = 0; i < itemsLength; i++) { totalSize += itemSize(i); } } else if (typeof itemSize === 'number') { totalSize = itemsLength * itemSize; } if ( scrollOffset > totalSize - containerHeight - threshold && hasNextPage && !isLoading ) { handleLoadMore(); } }, [ itemSize, threshold, isLoading, itemsLength, hasNextPage, handleLoadMore, containerHeight, isVariableSizeList, ], ); }