티스토리 뷰

 

페이지 네이션은 여러 컴포넌트에서 쓰일 수 있기 때문에 훅을 이용하는게 컴포넌트 재사용이라는 측면에서 유용하다.

 

그래서 구현중인데.. 쉽지않다..


1. 첫 시도

처음 getAllProductsAsync()로 usePagination.js에 { page, pageSize: PAGESIZE, orderBy }를 넘겨주고, 다음 버튼과 이전 버튼 내에 함수가 실행되도록 로직을 짰다.

// FleaMarketPage.js

function FleaMarketPage() {
  const [products, setProducts] = useState([]);
  const [orderBy, setOrderBy] = useState("recent");
  const [allProductsError, getAllProductsAsync] = useAsync(getItems);
  const [bestProductsError, getBestProductsAsync] = useAsync(getBestProducts);
  const [bestProducts, setBestProducts] = useState([]);
  const [totalCount, setTotalCount] = useState(0);

  // Pagination Custom Hook
  const {
    page,
    setPage,
    totalPage,
    pageNumbers,
    handleNextPage,
    handlePrevPage,
    handleClickPageNum,
  } = usePagination(1, totalCount, PAGESIZE, async () => {
    const result = await getAllProductsAsync({ page, pageSize: PAGESIZE, orderBy });
    return result;
  });
// usePagination.js

function usePagination(initialPage = 1, totalCount, pageSize, asyncFunction) {
  const [page, setPage] = useState(initialPage);
  const totalPage = Math.ceil(totalCount / pageSize);
  const pageNumbers = Array.from({ length: totalPage }, (_, index) => index + 1);

  const handleNextPage = async (...args) => {
    if (page < totalPage) {
      setPage((prev) => prev + 1);
      return await asyncFunction(...args);
    }
  };

  const handlePrevPage = async (...args) => {
    if (page > 1) {
      setPage((prev) => prev - 1);
      return await asyncFunction(...args);
    }
  };

  const handleClickPageNum = async (number) => {
    if (number !== page) {
      setPage(number);
      return await asyncFunction(number);
    }
  };

  return {
    page,
    setPage,
    totalPage,
    pageNumbers,
    handleNextPage,
    handlePrevPage,
    handleClickPageNum,
  };
}

export default usePagination;
// api.js

export async function getItems({
  page = 1,
  pageSize = 10,
  orderBy = "recent",
}) {
  try {
    const query = `page=${page}&pageSize=${pageSize}&orderBy=${orderBy}`;
    const { data } = await axiosInstance.get(`products?${query}`);
    return data;
  } catch (error) {
    throw error;
  }
}

 

이렇게 짰더니 다음 버튼을 누르니 page=1이 호출되고 이전 버튼을 누르면 page=2가 호출된다.

 

지금와서 보니 말도 안되게 짜놓았다..

커스텀 훅 만드는 방법을 처음 배우고, 시간 내에 제출해야 하는 미션이라 강의에서 배웠던 코드 그대로 적용하면 오류가 안 생기지 않을까 하는 마음에 코드를 따라쳤었다.

 

논리적으로 생각하지 않고 작성한 코드는 나중에 더 고통스러운 수정과정을 겪어야 하는 것임을 깨달았다ㅎㅎ

 

잘못된 부분을 하나씩 짚어보았다.

 

1) 우선 getAllProductsAsync 함수를 사용한 부분은 useAsync 훅을 통해 비동기 처리되었기 때문에 usePagination.js에서 각 이벤트의 async await는 제거했다.

2) asyncFunction의 인자로 page를 넣어 현재 page값이 반영되도록 했다.

 

 

 

2. 두번째 시도

변경된 코드는 다음과 같다.

// usePagination.js

function usePagination(initialPage = 1, totalCount, pageSize, asyncFunction) {
  const [page, setPage] = useState(initialPage);
  const totalPage = Math.ceil(totalCount / pageSize);
  const pageNumbers = Array.from({ length: totalPage }, (_, index) => index + 1);

  const handleNextPage = =(...args) => {
    if (page < totalPage) {
      setPage((prev) => prev + 1);
      return asyncFunction(page);
    }
  };

  const handlePrevPage = =(...args) => {
    if (page > 1) {
      setPage((prev) => prev - 1);
      return asyncFunction(page);
    }
  };

  const handleClickPageNum = (number) => {
    if (number !== page) {
      setPage(number);
      return asyncFunction(number);
    }
  };

  return {
    page,
    setPage,
    totalPage,
    pageNumbers,
    handleNextPage,
    handlePrevPage,
    handleClickPageNum,
  };
}

export default usePagination;

 

하지만 동일한 문제가 발생했다.

분명 setPage(prev => prev + 1);로 이전 값을 참조하도록 짰지만 문제는 해결되지 않았다.

 

이유는 React의 상태 업데이트가 비동기적으로 이뤄지기 때문이다.

 

페이지 번호를 변경할 때 setPage 함수에 이전 상태를 기반으로 업데이트하는 함수를 전달하여 변경했지만, React의 상태 업데이트는 비동기적으로 이루어지므로 항상 이전 값이 표시된다. 

 

따라서 asyncFunction(page)을 호출할 때 page 값은 변경 전의 값을 가지게 된다. 만약 handleNextPage 함수가 호출되어 페이지를 1 증가시키고 asyncFunction(page)이 호출되면, asyncFunction이 호출되는 시점에서의 page 값은 변경 전의 값인 증가되기 이전의 값일 것이다.

 

결국 asyncFunction이 호출되는 시점에서의 page 값은 업데이트되기 전의 값으로 설정되므로 원하는 동작을 얻을 수 없다.

 

 

 

3. 마지막 시도

변경된 코드는 다음과 같다.

// usePagination.js

import { useState, useEffect } from 'react';

function usePagination(initialPage = 1, totalCount, pageSize, asyncFunction) {
  const [page, setPage] = useState(initialPage);
  const totalPage = Math.ceil(totalCount / pageSize);
  const pageNumbers = Array.from({ length: totalPage }, (_, index) => index + 1);

  const handleNextPage = () => {
    if (page < totalPage) {
      setPage(page + 1);
    }
  };

  const handlePrevPage = () => {
    if (page > 1) {
      setPage(page - 1);
    }
  };

  const handleClickPageNum = (number) => {
    if (number !== page) {
      setPage(number);
    }
  };

  useEffect(() => {
    asyncFunction(page);
  }, [page]);

  return {
    page,
    setPage,
    totalPage,
    pageNumbers,
    handleNextPage,
    handlePrevPage,
    handleClickPageNum,
  };
}

export default usePagination;

 

useEffect 훅을 사용해서 문제를 해결했다. 의존성 배열에 page를 넣어서 페이지 번호가 변경 될 때 마다 새로운 페이지 값을 기반으로 asyncFunction을 호출되도록 했다. 이렇게 했더니 원하는 대로 동작한다!

 

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함