はじめに
前回は gatsby-awesome-pagination というパッケージを使用してページネーションを実装しましたが今回は 公式 の内容を元に自前で実装する方法を解説します。
前回 と同様に基本的なものが揃っていてかつページネーションが実装されてい ない gatsby-starter-blog のテンプレートを元に解説します。
ファイルの移動
もともとあるsrc/pages/index.js
の内容をもとにページネーションを実装したいので、
src/pages/index.jsをsrc/templatesに移動してテンプレートとして使用します。
src/pages/index.js
↓ 移動
src/templates/index.js
gatsby-node.jsの修正
src/templatesディレクトリに移動したindex.jsでページネーション出来るようにデータを生成して渡していきます。
gatsby-node.jsを以下のように編集します。 前回 と合わせる形でデータを成形します。
gatsby-node.js
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.createPages = async ({ graphql, actions, reporter }) => {
...
const posts = result.data.allMarkdownRemark.nodes
if (posts.length > 0) {
// (1)
+ const createPagePath = (pageNumber) => (pageNumber === 1 ? "/" : `/page/${pageNumber}`)
// (2)
+ const postsPerPage = 2
// (3)
+ const numberOfPages = Math.ceil(posts.length / postsPerPage)
// (4)
+ Array.from({ length: numberOfPages }).forEach((_, i) => {
+ const page = i + 1
+ createPage({
+ path: createPagePath(page),
+ component: path.resolve('./src/templates/index.js'),
+ context: {
+ limit: postsPerPage,
+ skip: i * postsPerPage,
+ numberOfPages,
+ humanPageNumber: page,
+ previousPagePath: page - 1 >= 1 ? createPagePath(page - 1) : null,
+ nextPagePath: page + 1 <= numberOfPages ? createPagePath(page + 1) : null
+ }
+ })
+ })
...
}
}
(1)は各ページのURLを生成する関数で共通で使用したいので関数化しています。
(2)は1ページあたりの件数で初期状態で3件なので今回は1ページあたり2件としています。
(3)は全ページ数を生成しています。
(4)はGatsbyのAPIであるcreatePageを使用して全ページに対して動的にページを生成しています。 公式 とは少し変更しています。contextの内容がsrc/templates/index.jsに渡されます。
src/pages/index.jsの編集
gatsby-node.jsを編集し他ことによりcreatePageのcontextがsrc/pages/index.jsのコンポーネントにpropsのpageContextとして受け取れるようになります。 またこちらはのcontextの内容はページ内のQueryとしても受け取れるので以下のように修正します。 contextの形を合わせましたのでこちらは 前回 と同じです。
src/pages/index.js
export const pageQuery = graphql`
- query {
+ query($skip: Int!, $limit: Int!) {
site {
siteMetadata {
title
}
}
- allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
+ allMarkdownRemark(
+ sort: { fields: [frontmatter___date], order: DESC }
+ skip: $skip
+ limit: $limit
+ ) {
nodes {
excerpt
fields {
slug
}
frontmatter {
date(formatString: "MMMM DD, YYYY")
title
description
}
}
}
}
`
BlogIndexコンポーネントで以下のようにpageContextを受け取りPaginationコンポーネントに渡しています。 contextの形を合わせましたのでこちらも 前回 と同じです。
src/pages/index.js
import Seo from "../components/seo"
+import Pagination from "../components/pagination"
-const BlogIndex = ({ data, location }) => {
+const BlogIndex = ({ data, location, pageContext }) => {
const siteTitle = data.site.siteMetadata?.title || `Title`
const posts = data.allMarkdownRemark.nodes
return (
<Layout location={location} title={siteTitle}>
<Seo title="All posts" />
<Bio />
...
<Pagination pageContext={pageContext} />
</Layout>
)
}
src/components/pagination.jsの作成
最後にページネーション用のコンポーネントを作成します。 contextの形を合わせましたのでこちらも 前回 と同じです。
src/components/pagination.js
import React from "react"
import { Link } from "gatsby"
const style = {
marginRight: '5px'
};
const Pagination = ({ pageContext }) => {
const { numberOfPages, humanPageNumber, previousPagePath, nextPagePath } = pageContext;
const pages = Array.from({ length: numberOfPages }, (v, i) => i + 1);
return (
<>
{
previousPagePath
? <Link to={previousPagePath} style={style}>prev</Link>
: null
}
{
pages.map(page => (
humanPageNumber !== page
? <Link key={page} to={page === 1 ? "/" : `/page/${page}`} style={style}>{page}</Link>
: <span style={style}>{page}</span>
))
}
{
nextPagePath
? <Link to={nextPagePath} style={style}>next</Link>
: null
}
</>
);
}
export default Pagination
まとめ
今回は自前でページネーションを実装する方法を解説しました。 公式でも解説されていたので、ページネーションに関してはパッケージを使用しなくても比較的簡単に実装できることが分かったかなと思います。 gatsby-awesome-paginationは型チェックにFlowを用いておりtypescriptで使用する際は自前で型定義を用意しなければならなかったので、 これくらいであれば拡張することも考慮して自前で実装しても良いなと思いました。