目次
- はじめに
- ライブラリ(react-paginate)の導入
- ページネーションの実装
- おわりに
はじめに
こんにちは、スタメンでエンジニアをしている手嶋です。今回は「react-paginate」というライブラリを使用し、Reactでページネーションを実装する方法を紹介したいと思います。
ライブラリ(React-Pegination)の導入
まず「react-paginate」」の導入です。ページネーションのライブラリはいくつか選択肢があると思いますが、以下の観点から「react-paginate」を採用しました。
・実現したいUIに近かった事 ・ライブラリの更新頻度 ・直近のダウンロード数
選定の際には、openbaseが非常に参考になりました。 以下のコマンドでreact-paginateをプロジェクトに導入します。
yarn add react-paginate // tsの場合は以下で型も追加。 yarn add @types/react-paginate
これで導入が完了です。
ページネーションの実装
- 続いてコンポーネントの実装です。以下は、ページネーションとユーザー情報(UserTable)を描画するcomponentです。(スタイル等は割愛しています)
- このComponentからページネーションComponentを呼び出しています。
import React from 'react'; // componentのimport import UserTable from 'component/UserTable'; import Pagination from 'component/Pagination'; // Userの型定義 export type userTypes = { id: number, name: string } interface Props { users: userTypes[]; //Userテーブルに表示するユーザー情報 userSize: number; //ページ数を計算するために必要な全ユーザーの数 handleSearchUser: () => void; // ユーザーを検索する関数 setCurrentPageNumber: (page: number) => void; //ページネーションの番号をセットする関数 } // User一覧とページネーションを描画 const Users = (props: Props) => { const { users, userSize, handleSearchUser, setCurrentPageNumber } = props; return ( <div> <div> {users.length > 0 && <UserTable users={users} /> } </div> {userSize > 0 && ( <Pagination userSize={userSize} handleSearchUser={handleSearchUser} setCurrentPageNumber={setCurrentPageNumber} /> )} </div> ); }; export default Users;
ページネーションのComponentは以下です。導入した「react-paginate」に必須であるpropsを渡す事で描画できます。propsの詳細については後述しますが、基本的な流れは以下です。
- pageCountに表示したいページ数を渡します。(ここでは全データ件数を、1ページに表示したい数で割った数にしています。)
- 上記のpropsにより、添付のような数字付きのボタンが生成されます。これをクリックすると「onPageChange」というpropsに渡している関数(例のhandlePaginate)が実行されます。
- この関数の引数にページ番号が渡ってくるので、その番号を元にAPIを叩く処理を実行し、サーバーサイドから取得したいデータを返します。(引数は0始まりなので、+1しています。)
- 取得できたら上記の親コンポーネントである「index.tsx」に取得したデータ(users)を渡すことで、UserTableに描画したいデータが表示されます。
- ページネーションComponentは、あくまで指定の番号を返すだけなので、その番号を元にデータ取得する処理が必要です。
その他のpropsは後述しています。それぞれのDOMにClassNameを指定できるので、そのClassNameにスタイルを当てる事も可能です。今回は「pagination.css」というファイルでスタイルを実装し、Componentにimportしました。
import React from 'react'; import ReactPaginate from 'react-paginate'; //ライブラリの呼び出し import 'styles/pagination.css'; //カスタムスタイル用ファイルの呼び出し interface Props { userSize: number; //ページ数を計算するために必要な全ユーザーの数 handleSearchUser: () => void; // ユーザーを検索する関数 setCurrentPageNumber: (page: number) => void; //ページネーションの番号をセットする関数 } const ONE_PAGE_DISPLAY_USERS = 20; const LAST_DISPLAY_SIZE = 20; const AROUND_DISPLAY_PAGES = 5; const Pagination = (props: Props) => { const { userSize, handleSearchUser, setCurrentPageNumber } = props; const handlePaginate = (selectedItem: { selected: number }) => { const page = selectedItem.selected + 1; setCurrentPageNumber(page); // APIを叩きに行く処理 handleSearchUser(); }; const arrowIcon = (iconName: 'left' | 'right') => { return ( <i class="fas fa-chevron-${iconName}"></i> ); }; // ページ数の計算 const calculatePageCount = () => { return Math.ceil(userSize / ONE_PAGE_DISPLAY_USERS) }; // ページネーションを表示 return ( <div> <ReactPaginate pageCount={calculatePageCount()} marginPagesDisplayed={LAST_DISPLAY_SIZE} pageRangeDisplayed={AROUND_DISPLAY_PAGES} onPageChange={handlePaginate} containerClassName="pagination" pageClassName="page-item" pageLinkClassName="page-link" activeClassName="active" activeLinkClassName="active" previousLinkClassName="previous-link" nextLinkClassName="next-link" previousLabel={arrowIcon('left')} nextLabel={arrowIcon('right')} disabledClassName="disabled-button" /> </div> ); }; export default Pagination;
propsの詳細
公式ドキュメントによると、必須なのは上から3つのみです。
props | 内容 | 補足 |
---|---|---|
pageCount | 総ページ数 | 必須 |
marginPagesDisplayed | 終端に表示する件数 | 必須 |
pageRangeDisplayed | 選択位置の前後で表示する件数 | 必須 |
onPageChange | ページクリック時にハンドルするメソッド | APIを叩く関数を渡す |
containerClassName | pageのulタグに設定するclass | |
pageClassName | pageのliタグに設定するclass | |
pageLinkClassName | pageのaタグに設定するclass | |
activeClassName | 現在activeなpageに設定するclass | |
previousLabel | previousに設定するラベル名 | JSXを渡す事が可能 |
nextLabel | nextに設定するラベル名 | JSXを渡す事が可能 |
previousClassName | previousのliタグに設定するclass | |
nextLinkClassName | nextのliタグに設定するclass | |
previousLinkClassName | previousのaタグに設定するclass | |
nextLinkClassName | nextのaタグに設定するclass | |
disabledClassName | previous・nextが押せなくなった状態の表示 | |
breakLabel | pageの省略表示 | |
breakClassName | pageの省略表示のulタグに設定するclass | |
breakLinkClassName | pageの省略表示のliタグに設定するclass |
おわりに
今回はページネーションを「react-paginate」を使って実装してみました。自前でも実装できるとは思いますが、ページ数が増えた時の表示方法や、ボタンを押せない時のハンドリング等が簡単に実装できるのが導入のメリットだと思います。
スタメンでは一緒にプロダクト開発を進めてくれる仲間を募集しています! 興味を持っていただいた方は、是非下記の募集ページを御覧ください。