페이지네이션을 만드려고 찾아보다가
react-paginate 라이브러리를 선택해서 만들었다.
하지만,, 마음에 안든다..
가운데 ...도 거슬리고 페이지가 5개씩만 나오게 하고 싶은데 찾아보니 안된다고 한다.
라이브러리 선택하는 것도 잘 해야한다더니 그 말이 맞음,,
다시 페이지네이션을 만들어보기로 한다..
export default function Pagination({
totalItems,
itemCountPerPage,
pageCount,
onClick,
currentPage,
}: IPropsPage) {
const totalPages = Math.ceil(totalItems / itemCountPerPage); // 총 페이지 개수
const [start, setStart] = useState<number>(1); // 시작 페이지
{[...Array(pageCount)].map((_, i) => {
const pageNumber = start + i;
return (
pageNumber <= totalPages && (
<li
className={`${currentPage === pageNumber ? "select" : ""}`}
onClick={() => {
onClick(pageNumber);
}}
key={pageNumber}
>
{pageNumber}
</li>
)
);
})}
페이지는 pageCount만큼 나오게 한다 (내가 5로 지정해 둠)
페이지 숫자는 총 페이지 개수까지만 나오고
현재 페이지가 페이지 숫자와 같을 때 클래스를 추가해줬다. (현재 페이지 스타일 주기 위함)
onClick으로 페이지 이동
const noPrev = currentPage === 1; // 이전 페이지가 없는 경우
const noNext = currentPage === totalPages; // 다음 페이지가 없는 경우
useEffect(() => {
if (currentPage >= start + pageCount) { // 페이지 카운트 다음 페이지로 갈떄
setStart((prev) =>
Math.min(prev + pageCount, totalPages - pageCount + 1),
);
} else if (currentPage < start) { // 페이지 카운트 이전 페이지로 갈 떄
setStart((prev) => Math.max(prev - pageCount, 1));
}
}, [currentPage, pageCount, totalPages, start]);
const handlePrevClick = () => {
if (!noPrev) {
onClick(currentPage - 1);
}
};
const handleNextClick = () => {
if (!noNext) {
onClick(currentPage + 1);
}
};
<li
onClick={handlePrevClick}
className={`prev ${noPrev ? "disabled" : ""}`}
>
<FiChevronLeft style={{ verticalAlign: "text-top" }} />
</li>
<li
onClick={handleNextClick}
className={`next ${noNext ? "disabled" : ""}`}
>
<FiChevronRight style={{ verticalAlign: "text-top" }} />
</li>
다음 페이지 카운트로 넘어갈 때 (currentPage >= start + pageCount)
그 단락의 첫 숫자를 알아야 하니까 prev + pageCount를 해준다.
하지만 마지막 단락일경우
예를 들면 마지막 페이지(totalPages)= 8/ pageCount = 5이라 가정한다.
prev + pageCount는 6이지만 5개로 맞춰서 보여주기 위해
totalPages - pageCount + 1을 해준다 (8 - 5 + 1 = 4)
그래서 마지막 단락으로 오면 4, 5, 6, 7, 8 이 나오게 된다!
이전 페이지로 넘어갈 때 (currentPage < start)
prev 에서 pageCount만큼 뺀 숫자 (이전 페이지의 첫 숫자)로 돌아가지만
1페이지에서 1보다 작은 값을 가지면 안되니까
Math.max(prev - pageCount, 1) 1을 넣어줬다.
사용 방법
const PER_PAGE = 8; // 한 페이지에 보여줄 아이템 수
export default function CampingCard({ className }: IPropsList) {
const [list, setList] = useState<ICampingList[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [currentPage, setCurrentPage] = useState<number>(1); // 현재 페이지 번호
const [totalCount, setTotalCount] = useState<number>(0); // 전체 캠핑 아이템 수
...
const pageCount = Math.ceil(totalCount / PER_PAGE); // 전체 페이지 수 계산
const onClickPage = (selected: number) => {
setCurrentPage(selected);
};
...
{/* 페이지네이션 */}
{pageCount > 0 && (
<Pagination
totalItems={totalCount}
onClick={onClickPage}
currentPage={currentPage}
pageCount={5}
itemCountPerPage={PER_PAGE}
/>
)}
전체 코드
더보기
import styled from "@emotion/styled";
import { useState, useEffect } from "react";
import { FiChevronLeft, FiChevronRight } from "react-icons/fi";
const PageWrap = styled.ul`
display: flex;
justify-content: center;
text-align: center;
li {
line-height: 2.8rem;
min-width: 2.8rem;
height: 2.8rem;
cursor: pointer;
&.disabled svg {
stroke: #ccc;
}
&.select {
background: #67794a;
color: #fff;
border-radius: 50%;
}
}
`;
interface IPropsPage {
totalItems: number;
itemCountPerPage: number;
pageCount: number;
currentPage: number;
onClick: (selected: number) => void;
}
export default function Pagination({
totalItems,
itemCountPerPage,
pageCount,
onClick,
currentPage,
}: IPropsPage) {
const totalPages = Math.ceil(totalItems / itemCountPerPage); // 총 페이지 개수
const [start, setStart] = useState<number>(1); // 시작 페이지
const noPrev = currentPage === 1; // 이전 페이지가 없는 경우
const noNext = currentPage === totalPages; // 다음 페이지가 없는 경우
useEffect(() => {
if (currentPage >= start + pageCount) { // 페이지 카운트 다음 페이지로 갈떄
setStart((prev) =>
Math.min(prev + pageCount, totalPages - pageCount + 1),
);
} else if (currentPage < start) { // 페이지 카운트 이전 페이지로 갈 떄
setStart((prev) => Math.max(prev - pageCount, 1));
}
}, [currentPage, pageCount, totalPages, start]);
const handlePrevClick = () => {
if (!noPrev) {
onClick(currentPage - 1);
}
};
const handleNextClick = () => {
if (!noNext) {
onClick(currentPage + 1);
}
};
return (
<PageWrap>
<li
onClick={handlePrevClick}
className={`prev ${noPrev ? "disabled" : ""}`}
>
<FiChevronLeft style={{ verticalAlign: "text-top" }} />
</li>
{[...Array(pageCount)].map((_, i) => {
const pageNumber = start + i;
return (
pageNumber <= totalPages && (
<li
className={`${currentPage === pageNumber ? "select" : ""}`}
onClick={() => {
onClick(pageNumber);
}}
key={pageNumber}
>
{pageNumber}
<p>s: {start}</p>
<p>i: {i}</p>
<p>c: {currentPage}</p>
</li>
)
);
})}
<li
onClick={handleNextClick}
className={`next ${noNext ? "disabled" : ""}`}
>
<FiChevronRight style={{ verticalAlign: "text-top" }} />
</li>
</PageWrap>
);
}
'언어 > Next.js' 카테고리의 다른 글
[Next 고캠핑] 커스텀훅으로 alert창 만들기 (0) | 2024.07.27 |
---|---|
[Next 고캠핑] 드롭다운 선택으로 검색을 해보자 (타입스크립트) (0) | 2024.07.25 |
[Next 고캠핑] 입지 구분 아이콘으로 하기 (0) | 2024.07.17 |
[Next 고캠핑]공공데이터포털 API 불러오기 (0) | 2024.07.16 |
[react-select] 리액트 셀렉트 커스텀하기 (0) | 2024.07.13 |