-
Notifications
You must be signed in to change notification settings - Fork 1
Open
Labels
documentationImprovements or additions to documentationImprovements or additions to documentation
Description
설명
useInfiniteQuery 를 이용해 페이지네이션된 데이터를 가져옵니다.
const {
fetchNextPage,
fetchPreviousPage,
hasNextPage,
hasPreviousPage,
isFetchingNextPage,
isFetchingPreviousPage,
...result
} = useGetProducts(accessToken, options?)const {
data,
isLoading,
fetchNextPage,
fetchPreviousPage,
hasNextPage,
hasPreviousPage,
isFetchingNextPage,
isFetchingPreviousPage,
...result
} = useGetRecommendsProducts(accessToken, options?)사용예시
// home 에 적용됐습니다.
import useGetRecommendProducts from '@/libs/hooks/useGetRecommendsProducts';
const Main = () => {
const [products, setProducts] = useState([]);
const {
data,
isLoading: fetchingRecommends,
fetchNextPage,
hasNextPage,
} = useGetRecommendProducts(accessToken, {
onSuccess: (data) => {
let result = [];
for (let page of data.pages) {
result = [...result, ...page.content];
}
setProducts(result);
},
});
return (
{products?.map((product) => (
<LoanProduct key={product.id} product={product} />
))}
)정의
두 훅이 같은 방식으로 정의됐습니다.
import { useInfiniteQuery } from '@tanstack/react-query';
import React from 'react';
import { ax } from '../axiosClient';
const useGetRecommendProducts = (accessToken, options?) => {
return useInfiniteQuery(
['products'],
({ pageParam = 1 }) => ax.getRecommendsProducts(accessToken, pageParam),
Object.assign(
{
getNextPageParam: (lastPage, allPages) => {
if (lastPage.pageNumber >= lastPage.totalPages) return;
return lastPage.pageNumber + 1;
},
},
options
)
);
};
export default useGetRecommendProducts;useInfiniteQuery
- 이미 가지고 있는 데이터 세트에다가 추가적으로 데이터를 로드 해야될 경우 사용한다. (ex: 무한스크롤 or 페이지네이션 )
useInfiniteQuery를 사용할 때 알아야 하는 사실들data는 현재 infinite query data를 포함하는 객체이다.data.pages는 패치받아온 페이지들로 이뤄진 배열이다.data.pageParams는 페이지들을 패치할때 사용한 page params 로 이뤄진 배열이다.fetchNextPage와fetchPreviousPage눈 둘다 'now available'인 함수들getNextPageParam과getPreviousParam은 만약 추가로 더 가져올 데이터가 있는지 판단하는 옵션들이다. 이 정보는 쿼리 함수에 추가 파라미터로서 제공된다. (쿼리함수는 fetchNextPage와 fetchPreviousPage 호출로 덮어쓸 수도 있다.)hasNextPage/hasPreviousPage는 'now available'인지 나타내는boolean이다.(getNextPageParam/getPreviousPageParam이undefined를 리턴하지 않을 경우true가 된다는 말)isFetchingNextPage와isFetchingPreviousPage는 백그라운드의 refresh state 인지 state를 추가로 load할 수 있는지를 나타내는boolean이다
예시
// cursor 인덱스로 한번에 페이지를 세개씩 가져온다고 할 때
fetch('/api/projects?cursor=0')
// { data: [...], nextcursor: 3}
fetch('/api/projects?cursor=3')
// { data: [...], nextcursor: 6}
fetch('/api/projects?cursor=6')
// { data: [...], nextcursor: 9}
fetch('/api/projects?cursor=9')
// { data: [...] }- 이러한 정보로
Load More버튼 같은 걸 만들 수 있다.
주의!
getNextPageParam에서 리턴한pageParam데이터를 덮어쓰기를 원하는게 아니라면,fetchNextPage를 콜백 인자로 넣어서 호출하지 말자!
예를들어,<button onClick={fetchNextPage} />이런식으로 쓰지 말자.
import { useInfiniteQuery } from '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>
</>
)
}사용예시2
import { useSearchParams } from "react-router-dom";
import { useInfiniteQuery } from "react-query";
// fetcher 함수
function searchData(
category: string,
keyword: string,
page: number
) {
const json = await fetch(
`${BASE_PATH}/search/${category}?api_key=${API_KEY}&query=${keyword}&page=${page}`
).then((response) => response.json());
return json;
}
export default function Search() {
// 검색어를 제출하면 Search 가 랜더링 되는데, 검색어 키워드를 가져온다.
const [searchParams, setSearchParams] = useSearchParams();
const keyword = searchParams.get("keyword");
// 랜더링 될 영화들이 담길 state
const [movies, setMovies] = useState();
// useInfiniteQuery 적용
const {
data: movieInfiniteData,
fetchNextPage: movieFetchNextPage,
hasNextPage: movieHasNextPage,
isFetchingNextPage: movieIsFetchingNextPage,
} = useInfiniteQuery(
["searchMovie", keyword],
({ pageParam = 1 }) => searchData("movie", keyword, pageParam),
{
getNextPageParam: (lastPage, pages) => {
const { page, total_pages } = lastPage;
return page < total_pages ? page + 1 : undefined;
},
onSuccess: (movieInfiniteData) => {
let result = [];
for (let page of movieInfiniteData.pages) {
result = [...result, ...page.results];
}
setMovies(result);
},
}
);
return (
<Wrapper>
<Title>Movie SEARCH: {keyword}</Title>
{movies && <SearchedResults results={movies} />}
<MoreButton
onClick={() => movieFetchNextPage()}
disable={!movieHasNextPage || movieIsFetchingNextPage}>
{movieIsFetchingNextPage
? "추가 패칭중"
: movieHasNextPage
? "영화 더보기"
: "남은 영화가 없습니다."}
</MoreButton>
</Wrapper>
);
}useInfiniteQuery Hook
const {
fetchNextPage,
fetchPreviousPage,
hasNextPage,
hasPreviousPage,
isFetchingNextPage,
isFetchingPreviousPage,
...result
} = useInfiniteQuery(queryKey, ({ pageParam = 1 }) => fetchPage(pageParam), {
...options,
getNextPageParam: (lastPage, allPages) => lastPage.nextCursor,
getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor,
})옵션
- useInfiniteQuery 의 옵션들은 useQuery 훅이랑 같은데, 아래 내용이 추가된다.
| 옵션명 | 타입 | default | 설명 |
|---|---|---|---|
| queryFn | (context: QueryFunctionContext) => Promise | defaultQueryFn | - defaultQueryFn이 없는 경우에 필수 - 데이터 요청할때 사용하는 쿼리 함수 QueryFunctionContext는 queryKey와 pageParam:unknown|undefined변수를 갖는 객체이다- data와 pageParam(getNextPageParam을 사용시)을 리턴해야 한다. |
| getNextPageParam | (lastPage, allPages) => unknown | undefined | - 쿼리의 신규데이터를 받으면 getNextPageParam 함수에 infinite data list의 마지막 페이지와 페이지들의 full array가 인자로 전달된다 - single variable을 리턴해야 한다. 이 단일 변수는 쿼리함수의 마지막 옵셔널 파라미터에 전달된다. - undefined를 리턴시키면 다음페이지가 없음을 가리킨다. |
|
| getPreviousPageParam | (firstPage, allPages) => unknown | undefined | - 쿼리의 신규데이터를 받으면 getPreviousParam 함수에 infinite data list의 첫 페이지와 모든 페이지가 인자로 전달된다. - single variable 을 리턴해야 되는데, 이 단일 변수는 쿼리의 마지막 옵셔널 파라미터에 전달된다. -undefined를 리턴면 이전 페이지가 없다는 걸 가리킨다. |
리턴값
- useInfiniteQuery 의 리턴은 useQuery 훅이랑 동일한데, 아래 내용이 추가된다.
| 옵션명 | 타입 | default | 설명 |
|---|---|---|---|
| data.pages | TData[] | 모든 페이지를 담은 배열 | |
| data.pageParams | unknown[] | 모든 page params를 담은 배열 | |
| isFetchingNextPage | boolean | fetchNextPage로 다음 페이지가 패칭되는 동안 true |
|
| isFetchingPreviousPage | boolean | fetchPreviousPage로 다음 페이지가 패칭되는 동안 true |
|
| fetchNextPage | (options?: FetchNextPageOptions) => Promise | true | 이 함수는 results의 다음 page를 패치하게 해준다.options.pageParam: unknown는 특정한 page param을 사용해서 패칭하게 해준다.options.cancelRefetch: boolean가 true면 fetchNextPage를 반복 호출하면 fetchPage도 매번 호출되고 또한 이전의 호출은 무시된다. false면 fetchNextPage를 반복호출해도 첫번째 호출이 resolved가 되기전까지 기다린다. |
| fetchPreviousPage | (options?: FetchPreviousPageOptions) => Promise | 위와 비슷 | |
| hasNextPage | boolean | next page를 fetch할게 있으면 가 된다.(getNextPageParam옵션을 통해 정해짐) |
|
| hasPreviousPage | boolean | 위와 비슷 |
Metadata
Metadata
Assignees
Labels
documentationImprovements or additions to documentationImprovements or additions to documentation