useInfiniteScroll.ts 1.2 KB

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