React-Query Pagination

곽재훈·2024년 6월 21일
3

Supabase와 React-Query로 페이지네이션 구현하기

오늘은 페이지네이션에 대해 공부했다!

페이지네이션을 구현하기 위해서 Supabase에 더미데이터를 넣고 연습했다!

const getPosts = async(page=0, limit=10) => {
  return await fetch(`${BASE_URL}/posts?page=${page}&limit=${limit}`)
}

일반적으로 페이지네이션을 구현할 때는 url에 query parameter로 page와 limit 값을 전달하는 것이 일반적이다. 다만 이번에는 백엔드가 아니라 Supabase DB로 요청을 보내기 때문에 코드의 형태가 조금 다르다!

// supabase.js
import { createClient } from "@supabase/supabase-js";
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseKey = import.meta.env.VITE_SUPABASE_KEY;
export const supabase = createClient(supabaseUrl, supabaseKey);

먼저 supabse client를 먼저 export 해주고!

supabase에서는 이렇게 pagination을 위한 함수를 제공하고 있다!

// api.posts.js
/**
 *
 * @param {number} page 현재 페이지.
 * @param {number} limit 한 페이지에 보여줄 아이템 수.
 * @returns
 */
export const getPagenatedUsers = async (page, limit) => {
  const { data, error } = await supabase
    .from("users")
    .select("*")
    .range(page * limit, (page + 1) * limit - 1);
  return { data, error };
};

range()pagelimit을 잘 넣어주고

// App.jsx
function App() {
  const [page, setPage] = useState(0);
  const LIMIT = 5;

  const {
    data: users,
    error,
    isPending,
  } = useQuery({
    queryKey: ["users", page],
    queryFn: () => getPagenatedUsers(page, LIMIT),
  });

이렇게 useQuery를 사용할 때, queryKey의 dependency array에 변수까지 넣어주면 완성입니다!

<button onClick={() => setPage((prev) => prev + 1)}>
	다음 페이지
</button>

hasMore가 없을 경우

supabase를 활용해서 만들면서 한 가지 불편했던 점!
보통의 경우에는 pagination과 관련된 데이터를 요청할 때, hasMore로 대표되는 속성을 response에 같이 넣어서 보내준다.
예를 들어서, 내가 20페이지에 대한 요청을 보냈는데 만약 21페이지가 존재하지 않는다면 response의 안에 20페이지에 대한 정보를 보내줄 때 hasMorenextPage 같은 속성에 false가 할당되서 돌아온다.

const getPosts = async(page=0, limit=10) => {
  return await fetch(`www.my-web-page/posts?page=20&limit=10`)
}

// response
{
	data : [...], // 20페이지에 대한 데이터
	hasMore : false, // hasMore가 false면 다음 페이지가 없다는 응답.
}

그런데 supabase의 pagination은 다음 페이지의 존재 유무와 관련된 정보를 response에서 제공하지 않는다!

그래서 component 내부에 hasMore라는 state를 만들어서 직접 구현해보기로 했다.

import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useEffect, useState } from "react";
import { getPagenatedUsers } from "./api.posts";

// App.jsx
function App() {
  const queryClient = useQueryClient();
  const [page, setPage] = useState(0);
  const [hasMore, setHasMore] = useState(true);
  const LIMIT = 5;

  const {
    data: users,
    error,
    isPending,
  } = useQuery({
    queryKey: ["users", page],
    queryFn: () => getPagenatedUsers(page, LIMIT),
  });

  useEffect(() => {
    queryClient.prefetchQuery({
      queryKey: ["users", page + 1],
      queryFn: async () => {
        const response = await getPagenatedUsers(page + 1, LIMIT);
        if (response.data.length === 0) {
          setHasMore(false);
        } else {
          setHasMore(true);
        }
        return response;
      },
    });
  }, [page]);

useEffect 안에서 queryClient.prefetchQuery()를 활용해서 다음 페이지의 데이터를 미리 받아오는데, 만약 response.data의 길이가 0이라면 hasMorefalse로 바꾼다.

<button
	disabled={page === 0}
	onClick={() => setPage((prev) => prev - 1)}
>
	이전 페이지
</button>

<button disabled={!hasMore} onClick={() => setPage((prev) => prev + 1)}>
	다음 페이지
</button>

이렇게 page가 0일 때, hasMorefalse 일 때 각각 버튼을 비활성화시켜서 존재하지 않는 페이지로 요청을 보내는 것을 막았다.

profile
개발하고 싶은 국문과 머시기

0개의 댓글

관련 채용 정보