버튼은 페이지네이션의 기준이 되는 숫자를 통해 생성된다.
(ex : 10개씩 보여주어야 하고 총 67개가 있다면 7 페이지가 나와야 함. 변수 pageNumber 참조)
버튼을 만들 때에는 Array 생성자함수, fill, map 메서드를 통해 생성한다.
버튼에게 클릭할 때마다 현재페이지의 값을 갖게 만드는 함수를 전달하고, 현재페이지는 Context API에 등록된 스테이트이며 해당 페이지넘버 스테이트는 API URL의 page를 제어하는 스테이트이다.(3번 참조)
67개를 10개씩 끊는다면 마지막 배열은 10이 아닌 7이다. 그 부분에 대한 에러핸들링 필요.
import { useState } from "react";
import { createContext } from "react";
export const UtilContext = createContext({
currentPageNumber: 0,
getNewsOnPage: (number) => {},
increasePageNumber: () => {},
decreasePageNumber: () => {},
})
const UtilProvider = ({children}) => {
const [currentPageNumber,setCurrentPageNumber] = useState(1)
const getNewsOnPage = (number) =>{
setCurrentPageNumber(number);
}
const increasePageNumber = () => {
setCurrentPageNumber(prev => prev + 1);
}
const decreasePageNumber = () => {
setCurrentPageNumber(prev => prev - 1);
}
const utilContext = {
getNewsOnPage,
increasePageNumber,
decreasePageNumber,
currentPageNumber
}
return <UtilContext.Provider value={utilContext}>{children}</UtilContext.Provider>
}
export default UtilProvider
// RouteBundle.jsx
<Route path =":categories" element={
<UtilProvider>
<Categories/>
</UtilProvider>} />
import React, { useState } from 'react';
import { useContext } from 'react';
import { useEffect } from 'react';
import { useParams } from 'react-router-dom';
import CategoriesTemplete from '../components/CategoriesTemplete';
import useHttp from '../hooks/useHttp';
import { getHeadlineNews } from '../lib/api';
import { UtilContext } from '../store/UtilContext';
const Categories = () => {
const {currentPageNumber} = useContext(UtilContext)
const {sendRequest,status,data:categoryNews} = useHttp(getHeadlineNews);
const {categories} = useParams();
useEffect(()=>{
sendRequest(categories,currentPageNumber);
return () => sendRequest(categories);
},[sendRequest,categories,currentPageNumber])
if(status === 'pending'){
return <div class="text-xl absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2">Now Loading.........</div>
}
if(categoryNews === null ){
return <div>something wrong!</div>
}
return <CategoriesTemplete categories={categories} categoryNews={categoryNews} />
};
export default Categories;
import { useContext } from "react"
import { useState } from "react"
import { UtilContext } from "../store/UtilContext"
import PaginationView from "./PaginationView"
export default function Pagination({totalResults,articles,categories}) {
const pageNumber = articles.length === 10 ? Math.ceil(totalResults / articles.length) : Math.ceil(totalResults / 10)
const [page,setPage] = useState(1)
const {getNewsOnPage} = useContext(UtilContext)
return <PaginationView getNewsOnPage={getNewsOnPage} pageNumber={pageNumber} articles={articles} totalResults={totalResults} page={page} />
}
5.paginationView 컴포넌트에서 사용
import React from 'react';
import uuid from "react-uuid"
const PaginationView = ({pageNumber,totalResults,getNewsOnPage,page}) => {
return (
<div className="bg-slate-500 px-4 py-3 flex items-center justify-between rounded-md sm:px-6 absolute bottom-4">
<div className="flex justify-between sm:hidden ">
<button
href="#"
className="relative inline-flex items-center shrink px-2 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
>
Previous
</button>
<button
href="#"
className="ml-3 relative inline-flex items-center px-5 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
>
Next
</button>
</div>
<div className="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
<div>
<p className="text-sm text-primary pr-4">
Showing <span className="font-medium">1</span> to <span className="font-medium">10</span> of{' '}
<span className="font-medium">{totalResults}</span> results
</p>
</div>
<div>
<nav className="relative z-0 inline-flex rounded-md shadow-sm -space-x-px" aria-label="Pagination">
<button
href="#"
className="relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"
>
<span className="sr-only">Previous</span>
<div className="h-5 w-5 font-bold" aria-hidden="true">←</div>
</button>
{/* create button */}
{Array(pageNumber).fill().map((_,i) =>(<button
key={uuid()}
aria-current={page}
className="z-10 bg-primary border-slate-300 text-primary relative inline-flex items-center px-4 py-2 border text-sm font-medium"
onClick={()=>getNewsOnPage(i + 1)}
>
{i+1}
</button>))}
<button
href="#"
className="relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"
>
<span className="sr-only">Next</span>
<div className="h-5 w-5 font-bold" aria-hidden="true">→</div>
</button>
</nav>
</div>
</div>
</div>
);
};
export default PaginationView;
경로가 바뀔 때 다시 currentPageNumber를 초기화 해야한다.
예를들어, science section에서 4번페이지를 보고 있다가, sports section으로 이동할 때 아무 처리도 하지 않는다면 sports section으로 이동할 때 sports section의 4번 페이지로 이동하게 된다.
useLocation을 통해 pathname이 변경될 때마다 1로 값이 바뀌게 변경한다.
const {pathname} = useLocation()
useEffect(()=>{
getNewsOnPage(1)
},[pathname])