[React] 리액트 페이지네이션 :: offset 방식
글 작성자: 망고좋아
반응형
🎯 페이지네이션 :: offset 방식
- 페이지네이션을 구현하는 방법 중 하나인 offset 방식을 알아보자.
📝 페이지네이션 핵심 코드 살펴보기
{getPageNumbers(currPage, pageCount).map((page) => {
return (
<Button
key={`pagination-button-${page}`}
active={currPage === page}
onClick={() => onClickPage(page)}
>
{page+1}
</Button>
)
})}
- 현재 페이지와 page가 같다면 active가 true로 되면서 배경색 설정
- onClick 하면 현재 페이지로 상태 변경
function getPaginationArray(currentPage, total) {
const resultList = [currentPage];
let idx = 1;
while (resultList.length < Math.min(9, total)) {
if (currentPage - idx > -1) resultList.unshift(currentPage - idx);
if (currentPage + idx < total) resultList.push(currentPage + idx);
idx++;
}
return resultList;
}
- 현재 페이지와 끝 페이지 숫자를 받고
- resultList에 현재 페이지인 currentPage를 넣어준다.
- 페이지네이션을 9개로 자를 거니까 while문을 resultPages.length < 9 만큼 돌려준다.
- 현재 페이지가 끝페이지보다 작으면 currentPage + idx을 push
- 현재 페이지가 -1보다 크면 currentPage - idx을 push
- 예를 들면 현재 페이지가 첫 페이지면 currentPage는 0으로 넘어오기 때문에 if (currentPage + idx < total) 문만 동작하기 때문에 1,2,3,4,5,6,7,8,9로 배열이 들어가고 return 된다.
- 만약 현재 페이지가 6이면 currentPage는 5로 넘어오기 때문에
- 5
- 4, 5 ,6
- 3, 4, 5, 6, 7
- 2, 3, 4, 5, 6, 7, 8
- 1, 2, 3, 4, 5, 6, 7, 8, 9
- 이렇게 while문이 돌아서 들어간다.
useEffect(() => {
(async function () {
if (currTab === "트랙") {
// 페이지네이션이 바뀔 때 마다 offset이 url 바뀌도록
const API_END_POINT = "https://api-xxxx/";
const offset = currPage * 6;
const trackUrl = `${API_END_POINT}track/list/?offset=${offset}&count=6`;
const response = await axios.get(trackUrl);
setTotalCardCount(response.data.track_count);
setCardData(response.data.tracks);
}
if (currTab === "과목") {
const API_END_POINT = "https://api-xxxx/";
const offset = currPage * 8;
const courseUrl = `${API_END_POINT}course/list/?offset=${offset}&count=8`;
const response = await axios.get(courseUrl);
setTotalCardCount(response.data.course_count);
setCardData(response.data.courses);
}
})();
}, [currTab, currPage]);
- 현재 tab과 현재 페이지가 변경될 때마다 useEffect를 실행시켜 offset을 가져온다.
- 트랙 탭 경우 offset을 기준으로 6개씩 가져온다.
<Pagination
currPage={currPage}
pageCount={Math.ceil(totalCardCount / (currTab === "트랙" ? 6 : 8))}
onClickPage={setCurrPage}
/>
- pageCount 전체 페이지 경우 전체 카드 개수에서 페이지에 뿌려줄 데이터만큼 나눈 다음 올림 처리하여 값 설정.
📝 전체 코드
📕 App.js
import axios from "axios";
import { useState, useEffect } from "react";
import styled from "styled-components";
import Tab from "./Tab.jsx";
import SearchTextField from "./SearchTextField.jsx";
import CardCount from "./CardCount.jsx";
import TrackCard from "./TrackCard.jsx";
import CourseCard from "./CourseCard.jsx";
import Pagination from "./Pagination.jsx";
const Container = styled.div`
display: flex;
align-items: center;
width: 1232px;
margin: auto;
padding-top: 50px;
flex-direction: column;
`;
const TracksContainer = styled.div`
display: grid;
grid-template-columns: repeat(3, 398px);
grid-column-gap: 19px;
grid-row-gap: 32px;
`;
const CoursesContainer = styled.div`
display: grid;
grid-template-columns: repeat(4, 296px);
grid-column-gap: 16px;
grid-row-gap: 24px;
`;
export default function App() {
const [currTab, setCurrTab] = useState("트랙");
const [searchValue, setSearchValue] = useState("");
const [currPage, setCurrPage] = useState(0);
const [cardData, setCardData] = useState([]);
const [totalCardCount, setTotalCardCount] = useState(0);
const handleClickTab = (tab) => {
// 탭이 바뀌면 페이지네이션 페이지를 0으로 리셋
if (tab !== currTab) {
setSearchValue("");
setCurrPage(0);
};
setCurrTab(tab);
};
const handleChangeSearch = (val) => {
setSearchValue(val);
};
useEffect(() => {
(async function () {
if (currTab === "트랙") {
// 페이지네이션이 바뀔 때 마다 offset이 url 바뀌도록
const API_END_POINT = "https://api-xxxx/";
const offset = currPage * 6;
const trackUrl = `${API_END_POINT}track/list/?offset=${offset}&count=6`;
const response = await axios.get(trackUrl);
setTotalCardCount(response.data.track_count);
setCardData(response.data.tracks);
}
if (currTab === "과목") {
const API_END_POINT = "https://api-xxxx/";
const offset = currPage * 8;
const courseUrl = `${API_END_POINT}course/list/?offset=${offset}&count=8`;
const response = await axios.get(courseUrl);
setTotalCardCount(response.data.course_count);
setCardData(response.data.courses);
}
})();
}, [currTab, currPage]);
return (
<Container>
<Tab currTab={currTab} onClick={handleClickTab} />
<SearchTextField value={searchValue} onChange={handleChangeSearch} />
<CardCount count={totalCardCount} />
{currTab === "트랙" ? (
<TracksContainer>
{cardData.map((track, i) => (
<TrackCard title={track.title} key={`track-card-${i}`} />
))}
</TracksContainer>
) : (
<CoursesContainer>
{cardData.map((course, i) => (
<CourseCard title={course.title} key={`course-card-${i}`} />
))}
</CoursesContainer>
)}
<Pagination
currPage={currPage}
pageCount={Math.ceil(totalCardCount / (currTab === "트랙" ? 6 : 8))}
onClickPage={setCurrPage}
/>
</Container>
);
}
📕 Pagination.js
import styled, { css } from "styled-components";
import Arrow from "./icons/Arrow.jsx";
const Wrapper = styled.div`
display: flex;
align-items: center;
justify-content: center;
margin-top: 64px;
`;
const Button = styled.button`
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
line-height: 22px;
color: #5e5f61;
min-width: 24px;
height: 24px;
background: transparent;
+ button {
margin-left: 4px;
}
${(props) =>
props.active &&
css`
color: #f9fafc;
background: #524fa1;
border-radius: 4px;
`}
`;
const ArrowWrapper = styled.button`
margin: ${(props) => (props.flip ? "0 0 0 15px !important" : "0 15px 0 0")};
${(props) =>
props.flip &&
css`
transform: scaleX(-1);
`}
svg {
display: block;
}
`;
Pagination.defaultProps = {
currPage: 0,
pageCount: 5,
onClickPage: () => {},
};
const MAX_PAGE_COUNT = 9;
function getPaginationArray(currentPage, total) {
const resultList = [currentPage];
let idx = 1;
while (resultList.length < Math.min(MAX_PAGE_COUNT, total)) {
if (currentPage - idx > -1) resultList.unshift(currentPage - idx);
if (currentPage + idx < total) resultList.push(currentPage + idx);
idx++;
}
return resultList;
}
export default function Pagination({ currPage, pageCount, onClickPage }) {
return (
<Wrapper>
<ArrowWrapper
onClick={() => currPage > 0 && onClickPage(currPage - 1)}
disabled={currPage <= 0}
>
<Arrow />
</ArrowWrapper>
{getPaginationArray(currPage, pageCount).map((page) => {
return (
<Button
onClick={() => onClickPage(page)}
key={`page-button-${page}`}
active={page === currPage}
>
{page + 1}
</Button>
);
})}
<ArrowWrapper
flip
onClick={() => currPage < pageCount - 1 && onClickPage(currPage + 1)}
disabled={currPage >= pageCount - 1}
>
<Arrow />
</ArrowWrapper>
</Wrapper>
);
}
반응형
'프로그래밍 > JavaScript' 카테고리의 다른 글
[JavaScript] 자바스크립트 async / await (0) | 2021.11.19 |
---|---|
[JavaScript] 자바스크립트 fetch (0) | 2021.11.19 |
[JavaScript] 자바스크립트 Promise (0) | 2021.11.17 |
[JavaScript] 자바스크립트 History API (0) | 2021.11.16 |
[JavaScript] 자바스크립트 변수의 유효범위, 렉시컬 환경 (0) | 2021.11.16 |
댓글
이 글 공유하기
다른 글
-
[JavaScript] 자바스크립트 async / await
[JavaScript] 자바스크립트 async / await
2021.11.19 -
[JavaScript] 자바스크립트 fetch
[JavaScript] 자바스크립트 fetch
2021.11.19 -
[JavaScript] 자바스크립트 Promise
[JavaScript] 자바스크립트 Promise
2021.11.17 -
[JavaScript] 자바스크립트 History API
[JavaScript] 자바스크립트 History API
2021.11.16