React-query 페이지네이션 (Paginated Query)

SUN·2024년 7월 22일

React-query

목록 보기
8/9

1. 개요

한번에 불러올 데이터들을 설정하고 그 다음 데이터를 설정한 개수만큼 불러온다.

2. 사용

1) 세팅하기

  • api.js
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에 설정된 값에 맞게 사용해야한다.

  • homepage.js
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;
  • queryKey에 page를 추가해 page 별로 데이터를 캐싱
  • queryFn에 page 추가

결과는 page 0에 3개의 데이터만 보여준다.

2) 페이지 변경 버튼

리액트 쿼리 개발자 도구에서 데이터를 보면 hasMore 값이 있다. 다음 페이지가 있으면 true이고 없으면 false이다.

 <div>
    <button
      disabled={page === 0}
      onClick={() => setPage((old) => Math.max(old - 1, 0))}
    >
      &lt;
    </button>
    <button
      disabled={!postsData?.hasMore}
      onClick={() => setPage((old) => old + 1)}
     >
      &gt;
    </button>
 </div>

3) 로딩없는 부드러운 전환

다음 페이지로 넘어갈때 마다 로딩 메시지가 뜬다. 새 페이지에 쿼리를 보내면 새로운 쿼리로 인식하기 때문에 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))}
      >
        &lt;
      </button>
      <button
        disabled={isPlaceholderData || !postsData?.hasMore}
        onClick={() => setPage((old) => old + 1)}
      >
        &gt;
      </button>
    </div>

isPlaceholderData를 사용하여 버튼 막기

4) 미리 데이터를 fetch하기(prefetch)

미리 다음 데이터를 fetch해 더 자연스럽게 데이터를 보여줄 수 있다.

기존 쿼리를 수정하지않고 아래 코드를 추가해준다.

...

useEffect(() => {
  if (!isPlaceholderData && postsData?.hasMore) {
    queryClient.prefetchQuery({
      queryKey: ['posts', page + 1],
      queryFn: () => getPosts(page + 1, PAGE_LIMIT),
    });
  }
}, [isPlaceholderData, postsData, queryClient, page]);
...
profile
안녕하세요!

0개의 댓글