File size: 2,001 Bytes
f0743f4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import throttle from 'lodash/throttle';
import React, { useCallback, useEffect, useRef } from 'react';
import type { FetchNextPageOptions, InfiniteQueryObserverResult } from '@tanstack/react-query';

export default function useNavScrolling<TData>({
  nextCursor,
  isFetchingNext,
  setShowLoading,
  fetchNextPage,
}: {
  nextCursor?: string | null;
  isFetchingNext: boolean;
  setShowLoading: React.Dispatch<React.SetStateAction<boolean>>;
  fetchNextPage?: (
    options?: FetchNextPageOptions | undefined,
  ) => Promise<InfiniteQueryObserverResult<TData, unknown>>;
}) {
  const scrollPositionRef = useRef<number | null>(null);
  const containerRef = useRef<HTMLDivElement | null>(null);

  // eslint-disable-next-line react-hooks/exhaustive-deps
  const fetchNext = useCallback(
    throttle(
      () => {
        if (fetchNextPage) {
          return fetchNextPage();
        }
        return Promise.resolve();
      },
      750,
      { leading: true },
    ),
    [fetchNextPage],
  );

  const handleScroll = useCallback(() => {
    if (containerRef.current) {
      const { scrollTop, clientHeight, scrollHeight } = containerRef.current;
      const nearBottomOfList = scrollTop + clientHeight >= scrollHeight * 0.97;

      if (nearBottomOfList && nextCursor != null && !isFetchingNext) {
        setShowLoading(true);
        fetchNext();
      } else {
        setShowLoading(false);
      }
    }
  }, [nextCursor, isFetchingNext, fetchNext, setShowLoading]);

  useEffect(() => {
    const container = containerRef.current;
    if (container) {
      container.addEventListener('scroll', handleScroll);
    }

    return () => {
      if (container) {
        container.removeEventListener('scroll', handleScroll);
      }
    };
  }, [handleScroll]);

  const moveToTop = useCallback(() => {
    const container = containerRef.current;
    if (container) {
      scrollPositionRef.current = container.scrollTop;
    }
  }, []);

  return {
    containerRef,
    moveToTop,
  };
}