Reactでのページネーション実装調べてたんだけど、Railsのkaminariみたいに「これ使っとけば間違いない」的なライブラリや実装が見つからなかったのでcustom hooks作ってオレオレ実装してみた。
前提
- React(Next.js)での実装
- フロントエンドとバックエンドは分離していてaxiosでバックエンドからjsonをgetして内容をrenderしている
- react-bootstrapライブラリを使ってBootstrapのページネーションを使っている
実装
- 現在のページと最後のページを保持するstateを作る
- そのstateを引数に取ってページネーションの番号を判定するカスタムフックを作る
顧客一覧を表示するページだとすると大体こんなん
マジックナンバー使っちゃってたりとリファクタリングの余地はあると思うけどだいたいこんな感じで切り出しとけば各componentで使い回せそう
pages/customers/index.tsx
import React, { useEffect, useState } from 'react' import { useCookies } from 'react-cookie' import { Pagination } from 'react-bootstrap' import axios from 'axios' import { useRouter } from 'next/router' import { CustomerParam } from 'interfaces/CustomerParam' import { usePaginationNumber } from 'hooks/usePaginationNumber' const Index = (): JSX.Element => { const [cookies] = useCookies(['_hoge_session']) const router = useRouter() const [customers, setCustomers] = useState<CustomerParam[]>([]) // Pagination用 // 表示するレコード数 const displayCount = 3 const [currentPage, setCurrentPage] = useState(1) const [lastPage, setLastPage] = useState(1000) let usePaginationNumberReturnVal = usePaginationNumber(currentPage, lastPage) let firstPaginationNum: number = usePaginationNumberReturnVal[0] let secondPaginationNum: number = usePaginationNumberReturnVal[1] let thirdPaginationNum: number = usePaginationNumberReturnVal[2] let forthPaginationNum: number = usePaginationNumberReturnVal[3] let fifthPaginationNum: number = usePaginationNumberReturnVal[4] useEffect(() => { const fetchCustomers = () => { axios.get( `${process.env.BACKEND_URL}/api/internal/customers`, { headers: { 'Session-Id': cookies._hoge_session }, params: { current_page: currentPage, display_count: displayCount } } ) .then(function (response) { setCustomers(response.data.customers) setLastPage(response.data.last_page) }) .catch(error => { console.log(error) }) } fetchCustomers() }, [router.query.public_id, cookies._hoge_session, currentPage, lastPage]) return ( <> {customers.map((customer, i) => { return ( <div key={i}> {customer.name} </div> ) })} <Pagination> <Pagination.First onClick={() => setCurrentPage(1)} /> {currentPage > 1 && <Pagination.Prev onClick={() => setCurrentPage(currentPage - 1)} />} <Pagination.Item active={currentPage == firstPaginationNum} onClick={() => setCurrentPage(firstPaginationNum)}>{firstPaginationNum}</Pagination.Item> {lastPage > 1 && <Pagination.Item active={currentPage == secondPaginationNum} onClick={() => setCurrentPage(secondPaginationNum)}>{secondPaginationNum}</Pagination.Item>} {lastPage > 2 && <Pagination.Item active={currentPage == thirdPaginationNum} onClick={() => setCurrentPage(thirdPaginationNum)}>{thirdPaginationNum}</Pagination.Item>} {lastPage > 3 && currentPage < lastPage && <Pagination.Item active={currentPage == forthPaginationNum} onClick={() => setCurrentPage(forthPaginationNum)}>{forthPaginationNum}</Pagination.Item>} {lastPage > 4 && currentPage < lastPage - 1 && <Pagination.Item active={currentPage == fifthPaginationNum} onClick={() => setCurrentPage(fifthPaginationNum)}>{fifthPaginationNum}</Pagination.Item>} {currentPage !== lastPage && <Pagination.Next onClick={() => setCurrentPage(currentPage + 1)} />} <Pagination.Last onClick={() => setCurrentPage(lastPage)} /> </Pagination> </> ) } export default Index
hooks/usePaginationNumber
import { useEffect, useState } from 'react' export const usePaginationNumber = (currentPage: number, lastPage: number) => { const [firstPaginationNum, setFirstPaginationNum] = useState(1) const [secondPaginationNum, setSecondPaginationNum] = useState(2) const [thirdPaginationNum, setThirdPaginationNum] = useState(3) const [forthPaginationNum, setForthPaginationNum] = useState(4) const [fifthPaginationNum, setFifthPaginationNum] = useState(5) useEffect(() => { setFirstPaginationNum(currentPage < 3 ? 1 : currentPage - 2) setSecondPaginationNum(currentPage < 4 ? 2 : currentPage - 1) let thirdNum = 3 if (currentPage === 1) { thirdNum = currentPage + 2 } else if (currentPage === 2) { thirdNum = currentPage + 1 } else if (currentPage === lastPage - 1) { thirdNum = lastPage - 1 } else if (currentPage === lastPage) { thirdNum = lastPage } else { thirdNum = currentPage } setThirdPaginationNum(thirdNum) let forthNum = 4 if (currentPage === 1 || (currentPage === 2 && lastPage > 5)) { forthNum = 4 } else if (currentPage === 2) { forthNum = 5 } else if (currentPage === lastPage - 2) { forthNum = lastPage - 1 } else { forthNum = currentPage + 1 } setForthPaginationNum(forthNum) let fifthNum = 5 if (currentPage === 1 || (currentPage === 2 && lastPage > 5)) { fifthNum = 5 } else if (currentPage === 2) { fifthNum = 6 } else if (currentPage === lastPage) { fifthNum = lastPage } else { fifthNum = currentPage + 2 } setFifthPaginationNum(fifthNum) }, [currentPage, lastPage]) return [ firstPaginationNum, secondPaginationNum, thirdPaginationNum, forthPaginationNum, fifthPaginationNum ] }