Reactでページネーションを実装

f:id:golazooo23:20201028141049p:plain

目次

  • はじめに
  • ライブラリ(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)が実行されます。

f:id:golazooo23:20201028135644j:plain

  • この関数の引数にページ番号が渡ってくるので、その番号を元に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」を使って実装してみました。自前でも実装できるとは思いますが、ページ数が増えた時の表示方法や、ボタンを押せない時のハンドリング等が簡単に実装できるのが導入のメリットだと思います。

スタメンでは一緒にプロダクト開発を進めてくれる仲間を募集しています! 興味を持っていただいた方は、是非下記の募集ページを御覧ください。

Webアプリケーションエンジニア募集ページ