3

I am trying to manage the index of the current page with state while implementing infinity scroll.

const [pageIndex, setPageIndex = useState<number>(2);

const getList = useCallback(async () => {
  try{
    // request api
    const {data} = await api(pageIndex);
    setPageIndex(index => index + 1);
  } catch(error) {
    // error handling
  }
}, [pageIndex]);

  const handleObserver = useCallback(
    async (
      [entry]: IntersectionObserverEntry[],
      observer: IntersectionObserver,
    ) => {
      if (entry.isIntersecting) {
        observer.unobserve(entry.target);
        await getList();
        observer.observe(entry.target);
      }
    },
    [getList],
  );

  useEffect(() => {
    if (!loadMoreRef.current) return;

    const option = {
      root: null,
      rootMargin: '0px',
      threshold: 0,
    };

    const observer = new IntersectionObserver(handleObserver, option);

    loadMoreRef.current && observer.observe(loadMoreRef.current);

    return () => observer && observer.disconnect();
  }, [handleObserver]);

I wrote the code as above, but useEffect runs 1 time, 2 times, 3 times, etc., increasing by 1, and the state update is not done properly.

I would appreciate it if you could tell me how to reference and update the latest state value inside intersectionObserver's callback function.

Hyeongrok
  • 31
  • 1
  • 5

1 Answers1

1

There seems to be a possible issue where updating the pageIndex state triggers all the callbacks to recompute and return new callback references, which eventually retriggers the useEffect callback. This might not be so ideal.

Assuming the useEffect hook and creating intersection observers logic is correct, I suggest using a React ref to cache the pageIndex state value such that its current value can be retrieved at any time in an enclosed callback handler. Remove all the dependencies from the useCallback hook so getList can be a stable reference for the life of the component.

Example:

const [pageIndex, setPageIndex = useState<number>(2);
const pageIndexRef = useRef<number>(pageIndex);

useEffect(() => {
  pageIndexRef.current = pageIndex;
}, [pageIndex]);

const getList = useCallback(async () => {
  try {
    // request api
    const { data } = await api(pageIndexRef.current);
    setPageIndex(index => index + 1);
  } catch(error) {
    // error handling
  }
}, []);
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • ```tsx useEffect(() => { pageIndexRef.current = pageIndex; }, [pageIndex]); ``` There seems to be a bit of a typo in the code, can I use it as above? – Hyeongrok Sep 23 '22 at 06:42
  • @Hyeongrok Oh, sorry, there was a typo. It should be `pageIndexRef.current = pageIndex;`. – Drew Reese Sep 23 '22 at 06:45