useInfiniteQuery는 페이지네이션이나 무한 스크롤과 같은 데이터 로딩을 구현할 때 사용됩니다.
1. useQuery 와 useInfiniteQuery의 차이
● useQuery
단일 데이터 객체 반환
한 번의 요청에 대한 응답만 저장
● useInfiniteQuery
pages 배열: 모든 페이지의 데이터를 순서대로 저장
pageParams 배열: 각 페이지를 가져올 때 사용된 매개변수 저장
여러 페이지의 데이터를 누적하여 저장
좀 더 이해하기 쉽게 data에 로그를 찍어보면
useQuery 와 uesInfiniteQuery 의 반환되는 data 의 구조는 다음과 같은 차이가 있습니다
1-1. useQuery
const { data } = useQuery({
queryKey: ['items'],
queryFn: fetchItems
});
console.log(data)
// 출력:
{
results: [...],
count: 100,
next: "url",
previous: null
}
1-2. uesInfiniteQuery
const { data } = useInfiniteQuery({
queryKey: ['items'],
queryFn: fetchItems
});
// data는 pages와 pageParams를 포함하는 객체
console.log(data)
// 출력:
{
pages: [
// 첫 번째 페이지 데이터
{
results: [...],
count: 100,
next: "url",
previous: null
},
// 두 번째 페이지 데이터
{
results: [...],
count: 100,
next: "url",
previous: "url"
},
// ... 추가 페이지들
],
pageParams: [undefined, "url", "url"] // 각 페이지를 가져올 때 쿼리함수(queryFn)에 전달되는 매개변수
}
useQuery 와는 달리 pages 란 배열에 담겨진 각 페이지에 보여줄 데이터들이 객체로 나눠져 담겨집니다.
따라서 useInfiniteQuery에서 데이터에 접근할 때는 보통 다음과 같이 합니다
data?.pages.map((page) => {
return page.results.map((item) => (
<Item key={item.id} {...item} />
));
});
2. uesInfiniteQuery 특징
- 페이지별 데이터 관리: 각 페이지의 데이터를 자동으로 관리
- 다음 페이지 정보 추적: queryFn의 pageParam을 통해 다음 페이지 정보를 추적
- 캐싱: 이미 불러온 데이터를 캐시에 저장
3. 기본 구조
const {
data, // 모든 페이지 데이터를 포함하는 객체
fetchNextPage, // 다음 페이지 로드 함수
hasNextPage, // 다음 페이지 존재 여부, boolean
isFetchingNextPage, // 다음 페이지 로딩 상태 boolean
} = useInfiniteQuery({
queryKey: ['items'],
queryFn: ({ pageParam = 초기값 }) => fetchUrl(pageParam),
getNextPageParam: (lastPage, allPages) => {
const nextPage = allPages.length + 1;
return nextPage <= lastPage.totalPages ? nextPage : undefined;
}
});
3- 1. getNextPageParam 추가 설명
- 이 쿼리에 대한 새 데이터가 수신되면 이 함수는 무한 데이터 목록의 마지막 페이지와 캐쉬에 저장된 모든 페이지의 전체 배열을 모두 수신합니다.
- 쿼리 함수에 마지막 선택적 매개변수로 전달할 단일 변수를 반환해야 합니다.
- getNextPageParam의 반환값이 다음 queryFn 호출 시의 pageParam 값이 됩니다.
// 첫 번째 페이지 로드
queryFn({ pageParam: 'https://api.example.com/page1' })
↓
getNextPageParam(lastPage) // returns 'https://api.example.com/page2'
↓
// fetchNextPage() 호출 시
queryFn({ pageParam: 'https://api.example.com/page2' })
↓
getNextPageParam(lastPage) // returns 'https://api.example.com/page3'
↓
// 계속...
- 다음 페이지가 없음을 나타내려면 undefined 를 반환합니다.
3-1-1. getNextPageParam 의 파라미터 allPages:
- 현재까지 로드된 모든 페이지 데이터를 포함하는 배열
- data.pages와 동일한 배열
- 각 요소는 queryFn이 반환한 페이지 데이터
3-2. hasNextPage
- getNextPageParam의 반환값을 기반으로 결정됨
- true: 다음 페이지가 있음 (getNextPageParam이 undefined가 아닌 값 반환)
- false: 마지막 페이지에 도달 (getNextPageParam이 undefined 반환)
3-3. isFetchingNextPage
- 다음 페이지 데이터를 가져오는 중인지 상태
- true: fetchNextPage() 호출로 새 페이지를 로딩 중
- false: 로딩이 완료되었거나 아직 시작하지 않음
4. 적용법
const fetchUrl = async (url) => {
const response = await fetch(url);
return response.json();
};
export function InfinitePeople() {
const {data, fetchNextPage, hasNextPage, isFetching, isLoading } = useInfiniteQuery({
queryKey: ['people'],
queryFn: ({pageParam = initialUrl}) => fetchUrl(pageParam),
getNextPageParam: (lastPage) => {
return lastPage.next || undefined
}
})
if (isLoading) return <div>로딩중...</div>
return (
<InfiniteScroll
loadMore={() => {
if(!isFetching) fetchNextPage()} }
hasMore={hasNextPage}
initialLoad={false} // 추가 설명 아래에 있습니다
>
{data.pages.map(pageData => {
return pageData.results.map(person =>
<Person
key={person.name}
name={person.name}
/>
)
})}
</InfiniteScroll>
)
}
여기서
if (isLoading) return <div>로딩중...</div>
이 없다면 처음 페이지를 렌더링할 때, data 는 undefined 이므로 data.pages 가 undefined 라는 오류가 발생하게된다
위처럼 로딩 안내를 하거나 data?.pages? 처럼 옵셔널 체이닝을 하면 오류가 해결되지만
사용자 경험측면에서 로딩상태 안내가 더 낫다고 판단되어 별도의 태그를 사용했다
initialLoad={false} 는 react-infinite-scroller 의 작동방식 때문에 추가로 설정하게 됬습니다.
추가로 정리하겠습니다.
https://daunje0.tistory.com/208
[React] react query와 react infinite scroller 사용 시 두 번째 페이지 중복 호출 이슈
react infinite scroller 라이브러리에는 initialLoad 속성이 true 로 기본 설정이 되어있습니다. react query 와 함께 사용할 경우, 1. 첫 페이지 데이터를 react query 가 가져오게 되고2. InfiniteScroll 컴포넌트
daunje0.tistory.com
무한스크롤 구현 시, 아래의 라이브러리를 사용 했습니다
https://www.npmjs.com/package/react-infinite-scroller
react-infinite-scroller
Infinite scroll component for React in ES6. Latest version: 1.2.6, last published: 3 years ago. Start using react-infinite-scroller in your project by running `npm i react-infinite-scroller`. There are 534 other projects in the npm registry using react-inf
www.npmjs.com