Gatsbyでページネーションを実装する(1)

Gatsbyでページネーションを実装する(1)

2021年07月07日

GatsbyJavascript
Gatsbyページネーション1

はじめに

今回はページネーションの実装方法を解説します。

公式 にも解説はありますが gatsby-awesome-pagination というパッケージがあり簡単に実装ができたので今回はこちらで解説します。

基本的なものが揃っていてかつページネーションは実装されていない gatsby-starter-blog のテンプレートをもとに解説します。

パッケージのインストール

以下のコマンドを実行してgatsby-awesome-paginationをインストールしてください。

$ npm install --save gatsby-awesome-pagination
or
$ npm i -S gatsby-awesome-pagination

ファイルの移動

今回はsrc/pages/index.jsの内容を元にページネーションを実装したいのですが、 src/pages内のindex.jsは自動的にルートページ(/)用のコンポーネントとして扱われ1ページになってしまうので 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`)
// (1)
+ const { paginate } = require('gatsby-awesome-pagination')

exports.createPages = async ({ graphql, actions, reporter }) => {
  const { createPage } = actions

  // Define a template for blog post
  const blogPost = path.resolve(`./src/templates/blog-post.js`)

  // Get all markdown blog posts sorted by date
  const result = await graphql(
    `
      {
        allMarkdownRemark(
          sort: { fields: [frontmatter___date], order: ASC }
          limit: 1000
        ) {
          nodes {
            id
            fields {
              slug
            }
          }
        }
      }
    `
  )

  if (result.errors) {
    reporter.panicOnBuild(
      `There was an error loading your blog posts`,
      result.errors
    )
    return
  }

  const posts = result.data.allMarkdownRemark.nodes

  if (posts.length > 0) {
// (2)
+    paginate({
+      createPage,
+      items: posts,
+      itemsPerPage: 2,
+      component: path.resolve('./src/templates/index.js'),
+      pathPrefix: ({ pageNumber }) => (pageNumber === 0 ? "/" : "/page"),
+    })
    ...
  }

gatsby-starter-blogにデフォルトで全件取得して各ページを作成する処理がありましたのでそちらに乗っかります。

(2)でgatsby-awesome-paginationからインポートしたpaginate関数にデータを渡しています。

paginate関数のオプションですが

items : ページネーション対象にしたいデータ。
itemsPerPage : 各ページに表示したい件数。今回は1ページあたり2件ずつ表示することになる。
component : ページを作成するために使用するテンプレート。index.jsが記事一覧ページとして使われていたので今回は流用しています。
pathPrefix : ページのurl設定。今回は1ページ目は「/」、2ページ目以降は「/page/2」、「/page/3」...となります。
createPage : 上記の設定を元に引数を作成してこのメソッドが呼ばれます。GatsbyにあるcreatePage関数を渡しており、こちらを呼び出すとページが作成される仕組み。

となっています。

gatsby-node.jsの編集は以上です。

src/pages/index.jsの編集

gatsby-node.jsを編集したことによりsrc/pages/index.jsのコンポーネントにcontextとしてデータが渡されるので、propsとしてpageContextが受け取ることができるようになります。 またこちらはページ内のQueryでも受け取れるので以下のように修正します。

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コンポーネントに渡しています。

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>
  )
}

pageContextは以下のようになっています。

pageNumber : 0始まりのページ番号。
humanPageNumber : 1始まりのページ番号。
skip : 記事を何個飛ばしているか。
limit : 1ページあたりの表示件数。
numberOfPages : 全ページ数。
previousPagePath : 前のページのurl。なければnullになる。
nextPagePath : 次のページのurl。なければnullになる。

このデータを元にページネーションを作っていきます。

src/components/pagination.jsの作成

最後にPaginationコンポーネントを作成します。 ページネーションの見せ方はいろいろありますが、今回は一番シンプルな全ページ表示する方法で実装します。

ページ数が多くなってくるとページネーションが破綻してしまいますが、そこは色々試してみてください。

ソースは以下となります。デザインは気にしないでください。

src/components/pagination.js

import React from "react"
import { Link } from "gatsby"

const style = {
  marginRight: '5px'
};

const Pagination = ({ pageContext }) => {
  // (1)
  const { numberOfPages, humanPageNumber, previousPagePath, nextPagePath } = pageContext;
  // (2)
  const pages = Array.from({ length: numberOfPages }, (v, i) => i + 1);

  return (
    <>
     // (3)
      {
        previousPagePath
          ? <Link to={previousPagePath} style={style}>prev</Link>
          : null
      }
      // (2)
      {
        pages.map(page => (
          humanPageNumber !== page
            ? <Link key={page} to={page === 1 ? "/" : `/page/${page}`} style={style}>{page}</Link>
            : <span style={style}>{page}</span>
        ))
      }
      // (3)
      {
        nextPagePath
          ? <Link to={nextPagePath} style={style}>next</Link>
          : null
      }
    </>
  );
}

export default Pagination

(1)でpageContextからnumberOfPages、humanPageNumber、previousPagePath、nextPagePathを取り出しています。

(2)でnumberOfPagesからページ配列を生成しそちらを元にページリンクを生成しています。

今回は1ページ目は「/」、それ以外は『/page/2」、「/page/3」...としているのでto={page === 1 ? "/" : `/page/${page}`}となっています。

(3)でpreviousPagePath,nextPagePathの値があるときは前へボタン、次へボタンを表示しています。

これで以下のようになります。

ページネーションサンプル

まとめ

今回はgatsby-awesome-paginationというパッケージでのページネーションの実装方法を解説しましたが、 公式にもある通り自前でページネーションを実装するにしても実際にやっていることはそこまで難しくなかったり、 型定義がなかったのでtypescriptで実装する際は自前で型定義を用意しないと行けなかったりするので、 次回は 公式 の内容を元に自前でページネーションを実装する方法について書こうと思います。

ホーム記事一覧プライバシーポリシーお問い合わせ
© 2021 luku.work