Infinite Scroll - Intersection Observer

wgnator·2022년 7월 17일
0

7/8 금

이번 프로젝트에서 무한 스크롤 적용을 맡게 되어 Intersection Observer 에 관하여 공부하게 되었다.

useIntersectionObserver.js - 훅으로 구현


const options = {
  root: null,
  rootMargin: "200px",
  threshold: 0,
};

function useIntersectionObserver(ref, callback, dependencies = []) {

  const onIntersection = ([entry], _observer) => {
    if (entry.isIntersecting) {
      callback();
    }
  };

  React.useEffect(() => {
    const observer = new IntersectionObserver(onIntersection, options);
    if (ref.current) {
      observer.observe(ref.current);
    }
    return () => observer.disconnect();
  }, dependencies);
  
  return [ref];
}
  1. Intersection Observer 는 Intersection 이 일어날때 취할 행위를 정의한 callback 함수와 intersection을 감지하기 위한 조건들을 명시하는 options 객체를 인자로 받는다.
  2. options:
    root - 감지할 요소의 부모 객체. root 인 경우 사용자의 viewport로 설정된다.
    rootMargin - 감지할 요소의 원래 위치보다 더 상단/하단 등에서 감지하고 싶을 경우 margin 을 줄 수 있다.
    threshhold - intersecting 이 일어난 후, 감지할 요소 크기의 몇 % 가 지나갔을 때 callback 을 호출할 것인지 정할 수 있다.
  3. observer.observe(감지할 요소) 가 호출되면 감시를 시작한다.
  4. useEffect 에서 사용하는 경우, 본 컴퍼넌트가 unmount 되었을때 옵저버도 같이 소멸되게 하기 위해 return 에서 observer.disconnect() 를 해준다.

사용 - Main.jsx

function Main() {
  const { movies, getMovies } = useMovieModel();
  const [pageNo, setPageNo] = useState(1);
  const endOfPageRef = useRef();
  const [isDataLoaded, setIsDataLoaded] = useState(false);
  const [hasReachedLastPage, setHasReachedLastPage] = useState(false);

  useEffect(() => {
    setIsDataLoaded(false);
    getMovies(pageNo).then((response) => {
      !response.data.results.length && setHasReachedLastPage(true);
      setIsDataLoaded(true);
    });
  }, [pageNo]);

  const [ref] = useIntersectionObserver(
    endOfPageRef,
    () => {
      if (isDataLoaded) setPageNo(pageNo + 1);
    },
    [movies]
  );

  return (
    <>
      <Contents movies={movies} />
      {hasReachedLastPage || movies.length < 20 ? <Footer /> : <EndOfPageDetector ref={ref} />}
    </>
  );
}

옵저버를 적용하면서 처음 직면했던 문제는 처음 데이터 로딩상태일때 이미 observe 대상인 페이지 끝단에 있을 detector 요소가 viewport 에 노출이 되어 callback 이 실행되 버리는 것이었다. 그래서 아직 영화 한 페이지가 로딩되기 전(movies.length < 20) 에는 나오지 않게 하는 조건을 추가하였다.

또한, 스크롤이 데이터의 끝에 도달한 경우 hasReachedLastPage 상태를 통해 detector 를 소멸하게 하였다.

profile
A journey in frontend world

0개의 댓글