【Next.js】Sequelizeでページング処理を実装する方法
Next.jsとsequelizeを使ってWEBアプリを作っているとページネーションを実装することが多いと思います。
前回は、Reactのフロントエンドのみの処理を紹介しました。なので今回は、サーバーサイドでWeb APIとDBを使った方法を紹介します。
利用するライブラリーは以下の通りです。
- Next.js(Web API)
- sequelize(DB)
- material-ui(ページネーション)
完成イメージ
サーバーサイドのデータ取得
まずはサーバーサイドの処理を紹介します。http://localhost:3000/api/book?page=「ページ番号」
にアクセスすると
- 5件に絞られた本のタイトル
- 総ページ数
この2つを取得するAPIを作ります。総ページ数はページネーションの一番右側の最終ページを表示するためのものです。
import book from "@model/bookModel"
export default async (req, res) => {
const PAGE_NUM = 5; //1ページに表示する件数
const offset_coefficient = !req.query || !req.query.page ? 0: req.query.page - 1; //ページ番号
//ユーザーに登録されている本を全て取得
const book_list = await book.findAndCountAll({
order: [
['book_id', 'ASC'] //作成日時でソート
],
limit: PAGE_NUM, //1ページ毎の件数
offset: PAGE_NUM * offset_coefficient //飛ばす件数
});
//総ページ数
book_list["count"] = Math.ceil(book_list["count"] / PAGE_NUM)
res.statusCode = 200
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify(book_list));
}
まずは、req.query.page
を取得して1を引いた値をoffset_coefficient に入れておきます。この値が開始位置を設定する係数になります。
そして、SequelizeのfindAndountAll
で対象のデータと総件数を取得します。limitで1ページ当たりの件数、offsetに先ほど取得したoffset_coefficient から計算した値をセットして5件のみ取得します。
findAndCountAll
で取得した値は以下のパラメータに入っています。
-
row
:取得データ -
count
:総件数
このcount
はリミットで絞った件数ではなくテーブルに入っているデータの件数になります。そのためcount
を1ページ当たりの件数5で割り、Math#ceilで切り上げを行うことで総ページ数を計算しているのです。
後は、取得したデータと総ページ数をレスポンスにセットして返すだけです。
クライアントサイドのページネーション設定
クライアントでは初期表示時とページ番号をクリックしたときにWeb APIにアクセスしてデータを取得して表示しています。
import MuiPagination from '@material-ui/lab/Pagination';
import { withStyles } from '@material-ui/core/styles';
import {useEffect, useState} from 'react'
export default function Index() {
const [page, setPage] = useState(1); //ページ番号
const [count, setCount] = useState(); //総ページ数
const [bookList, setBookList] = useState([]); //取得した本のリスト
//初回のみ実行
useEffect(async () => {
setBookListAPI(page);
}, []);
//ページ番号をクリックしたときの処理
const clickPage = (e, page) => {
setPage(page);
setBookListAPI(page);
}
//取得データのセットと総データ件数をセットする
const setBookListAPI = async (page) => {
const response = await fetch(`http://localhost:3000/api/book?page=${page}`);
const data = await response.json();
setBookList(data.rows); //取得データ
setCount(data.count); //総データ件数
}
const Pagination = withStyles({
root: {
display: 'inline-block', //中央寄せのためインラインブロックに変更
},
}) (MuiPagination);
return (
<>
<ul>
{bookList.map((book) => <li>{book.book_title}</li>)}
</ul>
<div style={{marginTop: "50px", textAlign: "center"}}>
<Pagination
count={count} //総ページ数
color="primary" //ページネーションの色
onChange={clickPage} //変更されたときに走る関数。第2引数にページ番号が入る
page={page} //現在のページ番号
/>
</div>
</>
);
}
useEffect
の第2引数に空配列を指定することで初回のみsetBookListAPI
が実行されるようになっています。そして、setBookListAPI
内では、APIからデータを取得して本のデータをbookList
、総ページ数をcount
のステータスに設定。bookList
は本のタイトルをリスト表示し、ページネーションに総ページ数count
を渡して最終ページ番号を表示しています。
次にページ番号が押されたときはonChange
メソッドでclickPage
を呼び出し、ページ番号をセットしています。後は初期表示時と同じでsetBookListAPI
を呼び出して本のデータを取得しています。
material-uiのページネーションについては前回の記事で紹介しているので、こちらを参考にしてください。
まとめ
-
findAndCountAll
を使うことで総件数と対象のデータを取得できる - クライアント側では初回表示時とページ番号が変更されたときにWeb APIを呼び出して表示データを更新する
Discussion