페이지네이션은 여러 post를 일정 개수로 나눠보여주는 기능'이다.
이에 맞게 페이지 인덱스를 생성하고, 페이지 인덱스 또는 사이드 화살표를 클릭해 해당 페이지로 이동할 수 있다.
페이지네이션을 처음 구현해보는 것이라 일단 구현에 필요한 조건들을 정리하는 게 어려웠다.
필요한 조건은 다음과 같다.
사실 처음에 리액트 쿼리를 쓰면 페이지네이션을 쉽게 구현할 수 있다는 정보를 접했다. 그래도 라이브러리없이 구현해보는 게 실력 향상에 나을 것 같아서 리액트 쿼리를 쓰지 않았다.
limit는 페이지네이션 바에서 보여주는 페이지 인덱스 개수 입니다.
1. FaAngleLeft(왼쪽 화살표):
페이지 인덱스가 1일 때, 페이지 인덱스가 가장 왼쪽이 아닐 때 disabled 된다.
현재 페이지 인덱스가 11이고, limit가 10일 때 해당 버튼 클릭 시 1~10 리스트를 보여준다. 2~9일 땐 disabled 된다.
2. FaAngleRight(오른쪽 화살표):
페이지 인덱스가 limit의 배수가 아닐 때, 페이지 인덱스가 가장 오른쪽이 아닐 때 disabled 된다.
현재 페이지 인덱스가 10이고, limit가 10일 때 해당 버튼 클릭 시 11~20 리스트를 보여준다.
3. 페이지 버튼 클릭 시 해당 버튼을 focus한다.
// main logic
interface PaginationProps {
totalPage: number;
limit: number;
currentPage: number;
setCurrentPage: SetState<number>;
}
const sliceArrayByLimit = (
totalPage: number,
limit: number,
currentPage: number,
) => {
const start = Math.max(1, currentPage - limit + 1);
const end = Math.min(totalPage, start + limit - 1);
return Array.from({ length: totalPage }, (_, i) => i + 1).slice(
start - 1,
end,
);
};
const Pagination = ({
totalPage,
limit,
currentPage,
setCurrentPage,
}: PaginationProps) => {
const [currentPageArray, setCurrentPageArray] = useState<number[]>(
sliceArrayByLimit(totalPage, limit, currentPage),
);
const handlePrevClick = () => {
const newCurrentPage = Math.max(1, currentPage - limit);
setCurrentPage(newCurrentPage);
setCurrentPageArray(sliceArrayByLimit(totalPage, limit, newCurrentPage));
};
const handleNextClick = () => {
const pageGroup = Math.ceil(currentPage / limit);
const newCurrentPage = limit * pageGroup + 1;
setCurrentPage(newCurrentPage);
setCurrentPageArray(sliceArrayByLimit(totalPage, limit, newCurrentPage));
};
return (
<PaginationWrapper>
<ButtonWrapper>
<FaAngleLeft
onClick={handlePrevClick}
disabled={currentPage === 1 || currentPage !== currentPageArray[0]}
/>
{currentPageArray.length > 0 &&
currentPageArray?.map((i) => (
<PageButton
key={i}
onClick={() => setCurrentPage(i)}
isActive={currentPage === i}
aria-current={currentPage === i ? 'page' : undefined}
>
{i}
</PageButton>
))}
<FaAngleRight
onClick={handleNextClick}
disabled={currentPage === totalPage || currentPage % limit !== 0}
/>
</ButtonWrapper>
</PaginationWrapper>
);
};
export default Pagination;
// 페이지네이션 사용 방법
const ITEM_PER_PAGE = 6;
const [currentPage, setCurrentPage] = useState(1);
const cardOffset = (currentPage - 1) * ITEM_PER_PAGE;
{data && (
<Pagination
totalPage={data.count}
limit={Math.floor(data.count / ITEM_PER_PAGE)}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
/>
)}