한번에 불러올 데이터들을 설정하고 그 다음 데이터를 설정한 개수만큼 불러온다.
export async function getPosts(page = 0, limit = 10) {
const response = await fetch(`${BASE_URL}/posts?page=${page}&limit=${limit}`);
return await response.json();
}
쿼리 파라미터로 page와 limit를 넘여주면 page의 데이터를 limit 개수만큼 보내도록 설계된다.
이때 page와 limit는 백엔드 api 자체에서 설정된 값으로 내가 사용하려는 api에 설정된 값에 맞게 사용해야한다.
import { useState } from 'react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { getPosts, uploadPost, getUserInfo } from './api';
const PAGE_LIMIT = 3;
function HomePage() {
// ...
const [page, setPage] = useState(0);
const {
data: postsData,
isPending,
isError,
} = useQuery({
queryKey: ['posts', page],
queryFn: () => getPosts(page, PAGE_LIMIT),
});
// ...
const posts = postsData?.results ?? [];
return (
<>
<div>
{currentUsername ? (
loginMessage
) : (
<button onClick={handleLoginButtonClick}>로그인</button>
)}
<form onSubmit={handleSubmit}>
<textarea
name="content"
value={content}
onChange={handleInputChange}
/>
<button disabled={!content} type="submit">
업로드
</button>
</form>
</div>
<div>
<ul>
{posts.map((post) => (
<li key={post.id}>
{post.user.name}: {post.content}
</li>
))}
</ul>
</div>
</>
);
}
export default HomePage;
결과는 page 0에 3개의 데이터만 보여준다.
리액트 쿼리 개발자 도구에서 데이터를 보면 hasMore 값이 있다. 다음 페이지가 있으면 true이고 없으면 false이다.
<div>
<button
disabled={page === 0}
onClick={() => setPage((old) => Math.max(old - 1, 0))}
>
<
</button>
<button
disabled={!postsData?.hasMore}
onClick={() => setPage((old) => old + 1)}
>
>
</button>
</div>
다음 페이지로 넘어갈때 마다 로딩 메시지가 뜬다. 새 페이지에 쿼리를 보내면 새로운 쿼리로 인식하기 때문에 pending 상태가 유지된다.
placeholderData를 사용하여 이전 데이터에서 자연스럽게 새데이터를 보여준다.
useQuery()에서 placeholderData 옵션에 keepPreviousData 혹은 (prevData) => prevData 사용
import {
// ...
keepPreviousData,
} from '@tanstack/react-query';
const {
data: postsData,
isPending,
isError,
} = useQuery({
queryKey: ['posts', page],
queryFn: () => getPosts(page, PAGE_LIMIT),
placeholderData: keepPreviousData,
});
하지만 다음 데이터가 오기까지 다음 페이지 버튼이 활성화된다면 다음버튼을 연타했을 때 존재하지 않는 페이지로 넘어가거가 원하는 페이지를 볼 수 없다.
<div>
<button
disabled={page === 0}
onClick={() => setPage((old) => Math.max(old - 1, 0))}
>
<
</button>
<button
disabled={isPlaceholderData || !postsData?.hasMore}
onClick={() => setPage((old) => old + 1)}
>
>
</button>
</div>
isPlaceholderData를 사용하여 버튼 막기
미리 다음 데이터를 fetch해 더 자연스럽게 데이터를 보여줄 수 있다.
기존 쿼리를 수정하지않고 아래 코드를 추가해준다.
...
useEffect(() => {
if (!isPlaceholderData && postsData?.hasMore) {
queryClient.prefetchQuery({
queryKey: ['posts', page + 1],
queryFn: () => getPosts(page + 1, PAGE_LIMIT),
});
}
}, [isPlaceholderData, postsData, queryClient, page]);
...