
tanstack-query 공식 문서 - useInfiniteQuery
codesandbox에서 useInfinityQuery 기본 코드 연습하기
주목할 변수는 fetchNextPage, pageParam, getNextPageParam 이다.
const {
fetchNextPage, // 다음 페이지를 불러오는 함수
fetchPreviousPage, // 이전 페이지를 불러오는 함수
hasNextPage, // 다음 페이지가 있는지 여부를 확인하는 함수
hasPreviousPage, // 이전 페이지가 있는지 여부를 확인하는 함수
isFetchingNextPage, // 다음 페이지를 불러오는 중인지 여부를 확인하는 변수
isFetchingPreviousPage, // 이전 페이지를 불러오는 중인지 여부를 확인하는 변수
...result // 쿼리 결과를 담고 있는 객체, 기타 옵션들과 함께 사용됨
} = useInfiniteQuery({
queryKey, // 쿼리를 식별하는 키
queryFn: ({ pageParam }) => fetchPage(pageParam), // 페이지 파라미터를 받아 해당 페이지를 가져오는 함수
initialPageParam: "", // v5에서 변경된 초기값 설정
...options,
getNextPageParam: (lastPage, allPages) => lastPage.nextCursor, // 마지막 페이지와 모든 페이지를 받아 다음 페이지의 파라미터를 반환하는 함수
getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor, // 첫 페이지와 모든 페이지를 받아 이전 페이지의 파라미터를 반환하는 함수
})
우선 lodash의 debounce를 이용해 useHandleScroll을 훅으로 만들었다.
// useHandleScroll.tsx
type fetchNextPageType = (
options?: FetchNextPageOptions | undefined
) => Promise<InfiniteQueryObserverResult<InfiniteData<any, unknown>, Error>>;
const useHandleScroll = (fetchNextPage: fetchNextPageType) => {
useEffect(() => {
window.addEventListener("scroll", scrollFn);
return () => {
window.addEventListener("scroll", scrollFn);
};
}, []);
const scrollFn = _.debounce(() => {
if (
window.innerHeight + document.documentElement.scrollTop >=
document.documentElement.offsetHeight - 10
) {
fetchNextPage();
}
}, 200);
return;
};
export default useHandleScroll;
그리고 무한 스크롤을 적용할 컴포넌트에서 다음과 같이 사용한다.
// MainPage.tsx
const {
status,
data: videoList, // data 변수명을 videoList로 변경
error,
fetchNextPage,
} = useInfiniteQuery({
queryKey: ["videoList", videoId],
queryFn: ({ pageParam }) =>
youtube.getVideoList(videoId, pageParam), // youtube api 요청 함수
initialPageParam: "",
getNextPageParam: (lastPage) => lastPage.nextPageToken || undefined,
});
useHandleScroll(fetchNextPage);
// api response 예시
{
items: data,
nextPageToken : 'abc',
pageInfo: {
totalResults: 200,
resultsPerPage: 50,
},
}
{
pages: string[],
pageParams: object[],
}
// videoList 예시
// 1) 처음 fetch 후
{
pages: [''],
pageParams: [
{
items: data,
nextPageToken : 'abc',
pageInfo: {
totalResults: 200,
resultsPerPage: 50,
},
}
]
}
// 2) 두 번째 fetch 후
{
pages: ['', 'abc'],
pageParams: [
{
items: data,
nextPageToken : 'abc',
pageInfo: {
totalResults: 200,
resultsPerPage: 50,
},
},
{
items: data2,
nextPageToken : 'def',
pageInfo: {
totalResults: 200,
resultsPerPage: 50,
},
}
]
}