Intersection observer

Intersection observer는 브라우저 뷰포트(Viewport)와 원하는 요소(Element)의 교차점을 관찰하며, 요소가 뷰포트에 포함되는지 아닌지 구별하는 기능을 제공한다. (더 쉽게는 특정 요소가 사용자 화면에 보이는지 안보이는지 판단함)

import { RefObject, useCallback, useEffect, useRef, useState } from 'react';

export const useInfiniteScroll = (targetElement: RefObject<HTMLDivElement>) => {
  const observerRef = useRef<IntersectionObserver | null>(null);
  const [intersecting, setIntersecting] = useState(false);

  const getObserver = useCallback(() => {
    if (!observerRef.current) {
      observerRef.current = new IntersectionObserver(entries =>
        setIntersecting(entries.some(entry => entry.isIntersecting))
      );
    }
    return observerRef.current;
  }, [observerRef.current]);

  useEffect(() => {
    if (targetElement.current) getObserver().observe(targetElement.current);

    return () => getObserver().disconnect();
  }, [targetElement.current]);

  return intersecting;
};

동작 과정

Untitled

const fetchMoreElement = useRef<HTMLDivElement>(null);
const intersecting = useInfiniteScroll(fetchMoreElement);

...

useEffect(() => {
    if (intersecting && hasNext) {
      fetcher('/test');
    }
  }, [intersecting]);

...
return (
    <>
      <Styled.Container ref={manRef}>
        {cards.map((data: any, idx) => (
          <Styled.Card key={data.description + idx}>
            <Styled.Content>
              <Styled.Title>{data.name + idx}</Styled.Title>
              {data.description}
            </Styled.Content>
          </Styled.Card>
        ))}
      </Styled.Container>
      <div style={{ position: 'absolute' }} ref={fetchMoreElement}>
        loading...
      </div>
    </>
  );