React + Laravel のページネーションについて
初めまして、Qiita初投稿です!
$ php artisan preset react
スカフォールドをvueからreactに切り替えている前提でお話します。
今回は以下のような地方のイベント一覧のサンプルを作成しました。
lalavelのページネーション機能について
今回laravel側のAPI実装についての説明は端折らせて頂きますが一応ページネーション機能についてはざっとおさらいします。
クエリビルダやEloquentの最後にpagenateメソッドを追加すると簡単に実装できます。
Eloquentの場合:モデル名::get()->paginate(1ページあたり表示させたい行数)
↑のようなJSONが返ってきます。
実際のデータはdataプロパティの中にpaginateで指定した数だけ入っています。

以下のメタ情報を使ってreactでページネーションを実装していきましょう。
キー | 内容 |
---|---|
current_page | 現在読み込んでいるページ |
per_page | 1ページあたりの取得件数(今回は6) |
total | 総件数 |
reactでのview実装
「react-js-pagination」ライブラリを使用して実装してみました。
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import Card from './Card';
import axios from 'axios';
import Pagination from 'react-js-pagination';
import './Index.css';
export default class Example extends Component {
constructor(props) {
super(props);
this.state = {
eventList: [],
activePage: 1,
itemsCountPerPage: 1,
totalItemsCount: 1,
pageRangeDisplayed: 10,
}
this.handlePageChange=this.handlePageChange.bind(this);
}
handlePageChange(pageNumber) {
axios
.get('/api/event?page=' + pageNumber)
.then(({data}) => {
this.setState({
eventList: data.data,
itemsCountPerPage: data.per_page,
totalItemsCount: data.total,
activePage: data.current_page,
})
});
}
componentDidMount() {
this.handlePageChange(1)
}
render() {
return (
<div className="container">
<h2>一覧</h2>
<div className="cardGroup">
<div className="container card-wrap">
<div className="row">
{this.state.eventList.map((item) => (<Card
key={item.id}
title={item.title}
imagePath={item.image_path}
startDate={item.start_date}
address={item.address}
/>))}
</div>
<Pagination
activePage={this.state.activePage}
itemsCountPerPage={this.state.itemsCountPerPage}
totalItemsCount={this.state.totalItemsCount}
pageRangeDisplayed={this.state.pageRangeDisplayed}
onChange={this.handlePageChange}
itemClass='page-item'
linkClass='page-link'
/>
</div>
</div>
</div>
);
}
}
react-js-paginationのパラメーターについて
<Pagination
activePage={this.state.activePage}
itemsCountPerPage={this.state.itemsCountPerPage}
totalItemsCount={this.state.totalItemsCount}
pageRangeDisplayed={this.state.pageRangeDisplayed}
onChange={this.handlePageChange}
itemClass='page-item'
linkClass='page-link'
/>
パラメータ名 | |
---|---|
activePage | 現在表示しているページ番号 |
itemsCountPerPage | ページあたりのアイテム数(今回は6) |
pageRangeDisplayed | !後述! |
totalItemsCount | アイテムの総数 |
onChange | ページ切り替えの時に呼ぶ関数(ページ番号を渡す) |
見た目のカスタマイズも可能! bootstrapのcssのclass名を入れています。
linkClass='page-link'
詳細はnpmを見てください
https://www.npmjs.com/package/react-js-pagination
これらをstateとして持たせますので初期値を入れておきます
constructor(props) {
super(props);
this.state = {
eventList: [],
activePage: 1,
itemsCountPerPage: 1,
totalItemsCount: 1,
pageRangeDisplayed: 5,
}
this.handlePageChange=this.handlePageChange.bind(this);
}
onChangeから呼び出すhandlePageChangeを作成
axiosを使用しています
ページ番号が引数として渡されるのでAPIのURLに?page=ページ番号のパラメータを付けてリクエストします。返ってきたデータはstateに入れましょう。
handlePageChange(pageNumber) {
axios
.get('/api/event?page=' + pageNumber)
.then(({data}) => {
this.setState({
eventList: data.data,
itemsCountPerPage: data.per_page,
totalItemsCount: data.total,
activePage: data.current_page,
})
});
}
ページ切り替えのボタンを押すとstateが更新され再レンダリングされます
<div className="row">
{this.state.eventList.map((item) => (<Card
key={item.id}
title={item.title}
imagePath={item.image_path}
startDate={item.start_date}
address={item.address}
/>))}
</div>
マウント時に初期表示するデータを取得する
componentDidMount() {
this.handlePageChange(1)
}
おまけCardコンポーネント作成
import React, {Component} from 'react';
export default class Card extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="col-lg-4">
<div className="card mb-4 box-shadow rounded-5">
<p className="group-badge">イベント</p>
<img className="card-img w-100 d-block rounded-0" style={{padding: 3}} src={this.props.imagePath}></img>
<div className="card-body">
<h4 className="card-title">{this.props.title}</h4>
<h6 className="text-muted card-subtitle mb-2"></h6>
<div className="card-detail"></div>
<div className="card-text">
<p>
<i className="fa fa-calendar"></i>
{this.props.startDate}
</p>
<p>
<i className="fa fa-map-marker"></i>
{this.props.address}
</p>
</div>
<a className="card-link" href="#">#仮タグ</a>
<a className="card-link" href="#">#仮タグ</a>
</div>
</div>
</div>
)
}
}
bootstrapStudioで作成しました。タグ多すぎ?cssを弄っているので、サンプル画像と表示が異なると思います。小コンポーネントへの受け渡しの参考までに。。