Skip to content

Infinite scroll 구현하기

SunHo Park edited this page Sep 13, 2022 · 2 revisions

Intersection Observer

Intersection Observer API는 타겟 요소와 상위 요소 또는 최상위 document 의 viewport사이의 intersection 내의 변화를 비동기적으로 관찰하는 방법이다.

https://developer.mozilla.org/ko/docs/Web/API/Intersection_Observer_API

브라우저의 viewport와 target으로 설정한 요소의 교차점을 관찰하여 그 target이 viewport에 포함되는지 구별하는 기능을 제공한다. 이를 통해 성능적으로 더 나은 무한 스크롤을 구현할 수 있게 된다.

생성하기

let options = {
  root: document.querySelector('#scrollArea'),
  rootMargin: '0px',
  threshold: 1.0
}

let observer = new IntersectionObserver(callback, options);

options

observer callback이 호출되는 상황을 조작한다. 기준값, 여백, 가시성(어디까지 보일 때 실행시킬지)를 설정할 수 있다.

callback

target이 만나졌을 때 callback이 실행된다. callback은 IntersectionObserverEntry 목록과 observer를 넘겨받는다.

useInfiniteQuery

import { useInfiniteQuery } from '@tanstack/react-query'

function Projects() {
  const fetchProjects = ({ pageParam = 0 }) =>
    fetch('/api/projects?cursor=' + pageParam)

  const {
    data,
    error,
    fetchNextPage,
    hasNextPage,
    isFetching,
    isFetchingNextPage,
    status,
  } = useInfiniteQuery(['projects'], fetchProjects, {
    getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
  })

  return status === 'loading' ? (
    <p>Loading...</p>
  ) : status === 'error' ? (
    <p>Error: {error.message}</p>
  ) : (
    <>
      {data.pages.map((group, i) => (
        <React.Fragment key={i}>
          {group.projects.map(project => (
            <p key={project.id}>{project.name}</p>
          ))}
        </React.Fragment>
      ))}
      <div>
        <button
          onClick={() => fetchNextPage()}
          disabled={!hasNextPage || isFetchingNextPage}
        >
          {isFetchingNextPage
            ? 'Loading more...'
            : hasNextPage
            ? 'Load More'
            : 'Nothing more to load'}
        </button>
      </div>
      <div>{isFetching && !isFetchingNextPage ? 'Fetching...' : null}</div>
    </>
  )
}

getNextPageParam에서 첫번째 매개변수는 fetch해온 데이터, 두번째 매개변수는 지금까지 가져왔던 page들을 배열이다. 이때 return해주는 값은 다음 값을 불러올 때 param으로 사용된다. pageParam으로는 숫자, 객체 모든 것이 들어갈 수 있음에 주의하자. {pageParms: { pageNumber: 1, keyword: ‘검색어'}} 형식으로도 사용이 가능하다

useIntersection + useInfiniteQuery

intersection이 일어날 때마다 infiniteQuery를 callback으로 넘겨주어 실행시키면 된다.