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>
</>
)
}
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>
);
}
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,
})
옵션명 | 타입 | 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를 리턴면 이전 페이지가 없다는 걸 가리킨다. |
옵션명 | 타입 | 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 | 위와 비슷 |