useInfiniteScroll.ts45 lines · main
1import { UIEvent, useCallback, useRef } from 'react'
2
3import { isAtBottom } from '@/lib/helpers'
4
5interface UseInfiniteScrollOptions {
6 isLoading?: boolean
7 isFetchingNextPage?: boolean
8 hasNextPage?: boolean
9 fetchNextPage: (options?: { cancelRefetch?: boolean }) => void
10}
11
12/**
13 * Returns a scroll handler that triggers fetchNextPage when the user scrolls
14 * to the bottom of a scrollable container. Includes horizontal scroll detection
15 * to avoid triggering loads when the user scrolls horizontally.
16 */
17export function useInfiniteScroll({
18 isLoading = false,
19 isFetchingNextPage = false,
20 hasNextPage = false,
21 fetchNextPage,
22}: UseInfiniteScrollOptions) {
23 const xScroll = useRef(0)
24
25 return useCallback(
26 (event: UIEvent<HTMLDivElement>) => {
27 const isScrollingHorizontally = xScroll.current !== event.currentTarget.scrollLeft
28 xScroll.current = event.currentTarget.scrollLeft
29
30 const shouldFetchNextPage =
31 !isLoading &&
32 !isFetchingNextPage &&
33 !isScrollingHorizontally &&
34 isAtBottom(event) &&
35 hasNextPage
36
37 if (!shouldFetchNextPage) {
38 return
39 }
40
41 fetchNextPage({ cancelRefetch: false })
42 },
43 [isLoading, isFetchingNextPage, hasNextPage, fetchNextPage]
44 )
45}