@spaced-out/ui-design-system
Version:
Sense UI components library
83 lines (73 loc) • 1.89 kB
Flow
// @flow strict
import * as React from 'react';
type InfiniteScrollOptions = {
itemSize: number | ((index: number) => number),
threshold: number,
itemsLength: number,
hasNextPage: boolean,
loadMoreItems: () => Promise<void>,
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,
],
);
}