티스토리 뷰
페이지 네이션은 여러 컴포넌트에서 쓰일 수 있기 때문에 훅을 이용하는게 컴포넌트 재사용이라는 측면에서 유용하다.
그래서 구현중인데.. 쉽지않다..
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을 호출되도록 했다. 이렇게 했더니 원하는 대로 동작한다!
'Error' 카테고리의 다른 글
- Total
- Today
- Yesterday
- 취업까지달린다
- innerhtml
- 프론트엔드
- react
- 리액트
- 객체
- html
- Target
- Git
- hydrationboundary
- 비제어 컴포넌트
- arguments
- 코드잇스프린트
- 코드잇 스프린트
- 유사배열객체
- GitHub
- javascript
- 제어 컴포넌트
- 동기
- js
- rest parameter
- 비동기
- Next.js
- CSS
- 중급 프로젝트
- 배열
- tanstackquery
- map
- currentTarget
- 스프린트프론트엔드6기
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |