React

[React] 리액트 무한스크롤 예제 및 동작 과정 설명

solfa 2024. 5. 27. 03:21

 

const observer = useRef();

...

const lastMovieElementRef = useCallback(
  (node) => {
    if (observer.current) observer.current.disconnect();
    observer.current = new IntersectionObserver((entries) => {
      if (entries[0].isIntersecting && hasMore) {
        setPage((prevPage) => prevPage + 1);
      }
    });
    if (node) observer.current.observe(node);
  },
  [hasMore]
);

동작 과정

  1. useCallback 설정: lastMovieElementRef는 useCallback 훅을 사용하여 정의된다. 이는 함수가 재생성되지 않고 메모이제이션되어 성능을 최적화한다. useCallback의 의존성 배열에 hasMore를 포함시켜 hasMore이 변경될 때만 함수가 재생성된다.
  2. node의 역할:
    • 초기화: 컴포넌트가 처음 렌더링될 때 node는 null이다.
    • 노드 할당: 특정 DOM 요소가 렌더링되고 ref가 할당될 때 node는 해당 DOM 요소를 가리키게 된다. 이 DOM 요소가 뷰포트에 들어올 때 Observer가 트리거된다.
  3. Intersection Observer 설정:
    • 기존 Observer 정리: observer.current.disconnect()를 호출하여 기존의 Intersection Observer 인스턴스를 정리한다. 이는 메모리 누수를 방지한다.
    • 새 Observer 생성: new IntersectionObserver를 사용하여 새로운 Intersection Observer 인스턴스를 생성하고 observer.current에 저장한다. 이 Observer는 entries 배열을 통해 관찰된 요소들의 가시성을 추적한다.
  4. Observer 연결:
    • 노드 관찰: if (node) observer.current.observe(node); 부분에서 node가 유효한 DOM 요소일 경우, 새로 생성된 Observer 인스턴스가 이 요소를 관찰하도록 설정한다. 이때 node는 ref를 통해 전달된 실제 DOM 요소를 참조한다.
      • ref 속성: 마지막 요소(movies.length === index + 1)에 lastMovieElementRef를 설정하여 해당 요소가 뷰포트에 들어오면 페이지를 증가시키도록 한다.

 

이후 렌더링할 때

return (
  <Center>
    {movies.map((movie, index) => (
      <MovieWrapper
        key={`${movie.id}-${index}`}
        onClick={() => handleImageClick(movie.id)}
        ref={movies.length === index + 1 ? lastMovieElementRef : null}
      >
        <Poster
          src={`https://image.tmdb.org/t/p/w200${movie.poster_path}`}
          alt={`Movie Poster ${movie.title}`}
        />
        <div>
          <Title>{movie.title}</Title>
          <Star>{movie.vote_average}</Star>
          <Overview>
            <p>{movie.overview}</p>
          </Overview>
        </div>
      </MovieWrapper>
    ))}
  </Center>
);

wrapper처럼 특정 요소에 할당하면 된다!

 

movies.length === index + 1: 현재 요소가 마지막 요소인지를 확인,

마지막 요소가 화면에 나타날 때만 Intersection Observer가 작동하게 하면서 스크롤을 계속 가능하게 하는 것~~

728x90