현재 진행중인 슈게팅
이라는 프로젝트에서 무한 스크롤을 구현할 일이 생겼다!
해당 블로그에서 무한스크롤 관련된 정보를 읽고
tanstackQuery에서 제공하는 useInfiniteQuery
를 써보고 싶어서 무한 스크롤 구현할 일만 기다리고 있었는데,,
마침 잘 되었다 싶어서 사용해보았다!
import { useInfiniteQuery } from '@tanstack/react-query'
import getAnimals from '../apis/getAnimals'
import { AnimalType, GenderType } from '../types/explore.type'
import { AnimalsResponse } from '../types/getAnimals.type'
import { animalClientToServer } from '../utils/animalUtil'
export const useGetAnimals = (gender: GenderType, animals: AnimalType) => {
return useInfiniteQuery<AnimalsResponse>({
queryKey: ['getAnimals', gender, animals],
queryFn: ({ pageParam = 0 }) => getAnimals(gender, animalClientToServer(animals), pageParam),
getNextPageParam: (lastPage, allPage) => {
return lastPage.page.number !== allPage[0].page.totalPages
? lastPage.page.number + 1
: undefined
},
staleTime: 60000 * 3,
})
}
getNextPageParam: (lastPage, allPages) => unknown | undefined
- When new data is received for this query, this function receives both the last page of the infinite list of data and the full array of all pages.
- It should return a single variable that will be passed as the last optional parameter to your query function.
- Return
undefined
to indicate there is no next page available.출처: https://tanstack.com/query/v4/docs/react/reference/useInfiniteQuery
해당 함수는
lastPage
: 인피니트 리스트의 제일 최근의 페이지에 담긴 정보를 넘겨줌
(해당 정보의 타입은 응답 타입으로 지정한 타입임 따라서 위의 코드에서는 AnimalsResponse
)
allPages
: 인피니트 리스트의 전체를 넘겨줌
(해당 정보의 타입은 AnimalsResponse[]
)
그리고 더 이상 로드 할 게 없다면, getNextPageParam
에 undefined
값을 넘겨주면된다
내가 적용한 방식에 대해서 추가적으로 설명해보겠다.
먼저, 서버 데이터가 넘어오는 구조는 다음 그림과 같다
number
: 현재 페이지 번호size
: 한 페이지 게시물 몇 개 불러오는지 (default: 16개)totalElements
: 총 게시물 갯수totalPages
: 총 페이지 갯수getNextPageParam: (lastPage, allPage) => {
return lastPage.page.number !== allPage[0].page.totalPages
? lastPage.page.number + 1
: undefined
},
→ 그래서 나는 페이지의 현재 넘버와 총 페이지 수를 비교해서 같이 않을 경우, + 1
한 값을 return 해주었다
→ 그리고 같을 경우에는 undefined
를 return 하여서 무한 스크롤을 멈춘다
이제 해당 훅을 실제 코드에 어떻게 적용했는지 알아보자!
const { data, fetchNextPage, hasNextPage, isLoading, isError } = useGetAnimals(
currentExploreFilter.gender,
currentExploreFilter.gender === 'female'
? currentExploreFilter.femaleAnimal
: currentExploreFilter.maleAnimal
)
이렇게 요소들을 불러와준다
<div className="flex flex-col h-full overflow-auto">
<InfiniteScroll
pageStart={0}
loadMore={() => fetchNextPage()}
hasMore={hasNextPage}
loader={
<div className="loader" key={0}>
Loading ...
</div>
}
useWindow={false}
>
<div className="flex w-screen justify-center">
<div className="flex flex-wrap gap-5 justify-start self-center w-[342px]">
{data?.pages.map((page) => {
return page.users.map((item, index) => (
<InformationTypeButton
nickname={item.nickName}
mbti={item.mbti}
key={index}
animal={animalServerToClient(item.animals)}
gender={item.gender.toLowerCase() as GenderType}
content={item.introduce}
onButtonClick={handlePopupSelected}
></InformationTypeButton>
))
})}
</div>
</div>
</InfiniteScroll>
</div>
→ 'react-infinite-scroller’
라는 라이브러리를 활용하면 직접 스크롤 이벤트를 추적해서 새로운 함수를 불러오지 않아도 된다! 매우 편리…
해당 라이브러리에는 쓰로틀링
이 내장되어있다고 한다
쓰로틀링 관련 벨로그 링크
react-infinite-scroller npm 사이트
⛔️ 주의할 점(1)은 typescript
를 사용하면 types
도 같이 설치해줘야한다 ⛔️
yarn add @types/react-infinite-scroller
⛔️ 주의할 점(2)는 부모의 높이에 height 설정 + overflow: auto
설정을 해줘야한다⛔️
<div className="flex flex-col h-full overflow-auto">
<InfiniteScroll
pageStart={0}
loadMore={() => fetchNextPage()}
hasMore={hasNextPage}
loader={
<div className="loader" key={0}>
Loading ...
</div>
}
useWindow={false}
>
사용 방법은 다음과 같이 훅 호출을 통해서 불러온 함수들을 이렇게 넣어주기만 하면 된다!
pageStart={0}
loadMore={() => fetchNextPage()}
hasMore={hasNextPage}
다음은 데이터를 이용해서 컴포넌트들을 불러오는 코드이다
{data?.pages.map((page) => {
return page.users.map((item, index) => (
<InformationTypeButton
nickname={item.nickName}
mbti={item.mbti}
key={index}
animal={animalServerToClient(item.animals)}
gender={item.gender.toLowerCase() as GenderType}
content={item.introduce}
onButtonClick={handlePopupSelected}
></InformationTypeButton>
))
})}
const { data
} = useGetAnimals(
currentExploreFilter.gender,
currentExploreFilter.gender === 'female'
? currentExploreFilter.femaleAnimal
: currentExploreFilter.maleAnimal
)
위와 같이 훅에서 호출한 data
에서는 pages
/ pageParams
를 반환한다
그래서 pages
에 대한 map
을 돌리고 거기서 page
에 대한 map
을 한 번 더 돌려야 한다!
그러면 원하는대로 무한 스크롤이 아주 잘 작동할 것이다!!