GatsbyJSにページネーションを追加した

昨日、久々に記事を書いたら、トップページで記事数が多すぎるのが気になったのでページネーションを追加した。

GatsbyJSはReact製なので、まさに最近いじっているからこのブログへ移行した当初よりReactは書けるようになったし、楽ちんと思いきや、GatsbyJSの仕組みそのものを忘れ去っていたのでかなり時間を溶かしてしまった。

gatsby-awesome-pagination をインストール

bash
yarn add gatsby-awesome-pagination

gatsby-node.js を修正

gatsby-node.js(抜粋)
const { paginate } = require("gatsby-awesome-pagination")

... // 省略

exports.createPages = async ({ graphql, actions }) => {
  
  ... // 省略

  const result = await graphql(`
    {
      allMarkdownRemark(
        sort: { order: DESC, fields: [frontmatter___date] }
        limit: 1000
      ) {
        nodes {
          id
          fields {
            slug
          }
        }
      }
    }
  `)

  ... // 省略

  const posts = result.data.allMarkdownRemark.nodes

  // ここで記事一覧を生成する
  paginate({
    createPage,
    items: posts,
    itemsPerPage: 10, // 10件ずつ表示する
    pathPrefix: ({ pageNumber }) => (pageNumber === 0 ? "/" : "/page"), 
    component: path.resolve(`./src/templates/index.js`),
  })

  ... // 省略

gatsby-awesome-paginationから paginate() 関数を読み込む。

componentには src/templates/index.js を指定する。 ここで注意すべきなのは、たしかgatsby-default-starterで作成していると src/pages/ に配置されていた気がするので src/templates/ に移動する必要があった。

gatsby-node.js の全体はこんな感じ

gatsby-node.js
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
const { paginate } = require("gatsby-awesome-pagination")

exports.onCreateNode = ({ node, getNode, actions }) => {
  const { createNodeField } = actions
  if (node.internal.type === `MarkdownRemark`) {
    const value = createFilePath({ node, getNode, basePath: `pages` })
    const url = node.frontmatter.path
    createNodeField({
      name: `slug`,
      node,
      value: url ? url : value,
    })
  }
}

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

  const result = await graphql(`
    {
      allMarkdownRemark(
        sort: { order: DESC, fields: [frontmatter___date] }
        limit: 1000
      ) {
        nodes {
          id
          fields {
            slug
          }
        }
      }
    }
  `)

  if (result.errors) {
    throw new Error(result.errors)
  }

  const posts = result.data.allMarkdownRemark.nodes

  // ここで記事一覧を生成する
  paginate({
    createPage,
    items: posts,
    itemsPerPage: 10,
    pathPrefix: ({ pageNumber }) => (pageNumber === 0 ? "/" : "/page"),
    component: path.resolve(`./src/templates/index.js`),
  })

  // ここで記事単体を生成する
  posts.forEach(post => {
    createPage({
      path: post.fields.slug,
      component: path.resolve(`./src/templates/article.js`),
      context: {
        slug: post.fields.slug,
      },
    })
  })
}

index.js を修正

src/templates/index.js を修正する。

props に pageContext を追加

src/templates/index.js(抜粋)
const IndexPage = ({ data, location, pageContext }) => {
  return (
    ... ///省略
  )
});

gatsby-node.jspaginate関数を使ってページを生成すると、そのページには自動的に pageContext が追加され、使えるようになる。 pageContext には下記プロパティが定義されている。

  • pageNumber - ページ番号(0始まり)
  • humanPageNumber - ページ番号(1始まり)
  • skip - GraphQL内で利用可能な $skipアイテム
  • limit - GraphQL内で利用可能な $limitアイテム
  • numberOfPages - 全ページ数
  • previousPagePath - 1個前のページパス。 pathPrefixで設定した形式になる。
  • nextPagePath - 1個先のページパス。 pathPrefixで設定した形式になる。

クエリ部分を修正

src/templates/index.js(抜粋)
... // 省略

export const query = graphql`
  query($limit: Int!, $skip: Int!) { //引数 $limit, $skip を追加
    allMarkdownRemark(
      sort: { fields: [frontmatter___date], order: DESC }
      filter: { frontmatter: { type: { ne: "article" } } }
      limit: $limit // 追加
      skip: $skip // 追加
    ) {
      totalCount
      edges {
        node {
          frontmatter {
            title
            year: date(formatString: "YYYY")
            daymonth: date(formatString: "MM/DD")
            categories
            tags
          }
          fields {
            slug
          }
          excerpt
        }
      }
    }
  }
`

GraphQLのクエリに $limit, $skipを追加する。

ページャーを追加

src/templates/index.js(抜粋)
<Link to={props.pageContext.previousPagePath}>前の10件</Link>
<Link to={props.pageContext.nextPagePath}>次の10件</Link>

pageContext が使えるようになったことで、上記コンポーネントを記事一覧ページ内に埋め込むと「前の10件」、「次の10件」へ移動するリンクを埋め込める。

なお、このブログはGatsbyブログにページネーションを導入する | Jack-s blogさまの記事を参考に、そのままMaterial-UI Pagination Componentsに渡して埋め込む手法にした。

参考にさせていただいたサイト

/ 以上