카테고리 없음

[2024-01-28] 개인 프로젝트 회고

이즈흐 2024. 1. 28. 17:43

1. useInfiniteQuery 사용하기!

TanStack Query에서 사용할 수 있는 useInfiniteQuery가 있는데

이 훅은 무한 스크롤이나 load more(더 보기)과 같이

특정 조건에서 데이터를 추가적으로 받아오는 기능을 구현할 때 사용하면 유용하다.

 

useInfiniteQuery 반환값

const {
  fetchNextPage,
  fetchPreviousPage,
  hasNextPage,
  hasPreviousPage,
  isFetchingNextPage,
  isFetchingPreviousPage,
  ...result
} = useInfiniteQuery({
  queryKey,
  queryFn: ({ pageParam }) => fetchPage(pageParam),
  initialPageParam: 1,
  ...options,
  getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) =>
    lastPage.nextCursor,
  getPreviousPageParam: (firstPage, allPages, firstPageParam, allPageParams) =>
    firstPage.prevCursor,
})

isFetchingNextPage, isFetchingPreviousPage, fetchNextPage, fetchPreviousPage, hasNextPage 등이 추가적으로 있다.

  • data.pages: 모든 페이지 데이터를 포함하는 배열이다.
  • data.pageParams: 모든 페이지 매개변수를 포함하는 배열이다.
  • fetchNextPage: 다음 페이지를 fetch 할 수 있다.
  • fetchPreviousPage: 이전 페이지를 fetch 할 수 있다.
  • isFetchingNextPage: fetchNextPage 메서드가 다음 페이지를 가져오는 동안 true이다.
  • isFetchingPreviousPage: fetchPreviousPage 메서드가 이전 페이지를 가져오는 동안 true이다.
  • hasNextPage: 가져올 수 있는 다음 페이지가 있을 경우 true이다.
  • hasPreviousPage: 가져올 수 있는 이전 페이지가 있을 경우 true이다.

주요 옵션

initialPageParam: TPageParam

  • initialPageParam을 이용해서 첫 페이지를 가져올 때 사용할 기본 페이지 매개변수이다. 필수값이다.

getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) => TPageParam | undefined | null

  • getNextPageParam 을 이용해서 페이지를 증가시킬 수 있다. 필수값이다.
    • getNextPageParam의 첫 번째 인자 lastPage는 fetch 해온 가장 최근에 가져온 페이지 목록이다.
    • 두 번째 인자 allPages는 현재까지 가져온 모든 페이지 데이터이다.
    • 세 번째 인자 firstPageParam 는 첫 번째 페이지의 매개변수이다.
    • 네 번째 인자 allPageParams 는 모든 페이지의 매개변수이다.

사용 가능한 다음 페이지가 없음을 표시하려면 undefined 또는 null을 반환하면 된다.

getPreviousPageParam도 존재하며, getNextPageParam와 반대의 속성을 갖고 있다.

 

maxPages: number | undefined

  • infinite 쿼리에 저장 할 최대 페이지 수이다.
  • 최대 페이지 수에 도달했는데 새 페이지를 가져오면 지정된 방향(next, previous)에 따라 페이지 배열에서 첫 번째 페이지 또는 마지막 페이지가 제거된다.
  • 0 또는 undefined라면 페이지 수는 무제한이다.

일단 실제 예제를 보면서 이해해보자

import { useInfiniteQuery } from '@tanstack/react-query'
import { getHerbList } from '~/api/herbList'
import { HerbList, HerbInfos } from '~/types/herbList'

const useGetHerbList = (pageNo = 1, numOfRows = 10) => {
  const { data, hasNextPage, isFetching, isFetchingNextPage, fetchNextPage } =
    useInfiniteQuery<HerbList>({
      queryKey: ['herb', { pageNo, numOfRows }],
      queryFn: ({ pageParam = pageNo }) =>
        getHerbList({ pageNo: pageParam as number, numOfRows }),
      initialPageParam: 1,
      getNextPageParam: (lastPage, allPages) => {
        if (lastPage.length < numOfRows) return undefined

        return allPages.length + 1
      }
    })

  const isHerb = {
    isHerb: true
  }
  const result = data?.pages.flat().map((d: HerbInfos) => ({ ...d, ...isHerb }))
  return {
    herbList: result || [],
    isFetching,
    isFetchingNextPage,
    hasNextPage,
    fetchNextPage
  }
}

export default useGetHerbList

 

console.log(allPages를 했을 때 나오는 로그다

이를통해 페이지 단위로 데이터를 갖고온다.

한번 더 갖고오면 이런식으로 형성된다.

그래서 getNextPageParam은 allPages의 length+ 1한 값을 반환해서 pageParams에 저장한다.

 getNextPageParam: (lastPage, allPages, allPageParams) => {
    if (lastPage.length < numOfRows) return undefined;

    return (allPageParams as number) + 1 
  }

그래서 allPageParams를 사용해도 된다.

즉, getNextPageParam의 반환 값이 pageParams로 들어간다.

 

maxPage는 뭐야??

https://jforj.tistory.com/378

 

[React] react-query v5 변경점 알아보기 (4) - useInfiniteQuery 기능 변경

안녕하세요. J4J입니다. 이번 포스팅은 react-query v5 변경점 알아보기 네 번째인 useInfiniteQuery 기능 변경에 대해 적어보는 시간을 가져보려고 합니다. 이전 글 [React] react-query v5 변경점 알아보기 (1) -

jforj.tistory.com

 

 

 

2. intersection Observer로 무한스크롤 구현하기!

1. useInfiniteScroll 커스텀 훅 만들기!

import { useEffect, useRef } from 'react'

const useInfiniteScroll = <T extends Element>(
  callback: () => void,
  options?: IntersectionObserverInit
) => {
  const ref = useRef<T>(null) //감시할 요소를 저장할 ref 생성

  // IntersectionObserver를 생성, callback 함수는 감시 대상이 화면에 나타날 때 호출된다.
  useEffect(() => {
    const observer = new IntersectionObserver(
      entries => entries.forEach(entry => entry.isIntersecting && callback()),
      options || { threshold: 0.5 }
    )

    //ref에서 현재 요소를 가져온다.
    const target = ref && ref.current
    
    // ref가 없으면 아무 작업도 하지 않고 종료 ref가 null이면 감시할 필요가없으니까
    if (!target) return

    // IntersectionObserver를 사용해 감시 대상을 감시
    observer.observe(target)

    // 컴포넌트가 언마운트되면 IntersectionObserver에서 감시를 해제
    return () => observer.unobserve(target)
  }, [callback, options])

  return { ref }
}

export default useInfiniteScroll

2. 만든 커스텀 훅을 사용!

import useInfiniteScroll from '~/hooks/useInfiniteScroll'

const CardListPage = () => {
  ...
  
  const { ref } = useInfiniteScroll<HTMLDivElement>(refetch)

  return (
      
      ...
      
      <div ref={ref}></div>

  )
}
export default CardListPage
728x90
반응형
LIST