お首が長いのよお首が長いのよ

チラシの裏よりお届けするソフトウェアエンジニアとして成長したい人のためのブログ

2021-08-22

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

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

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

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

bash: title=bash
1
2yarn add gatsby-awesome-pagination
3
4

gatsby-node.js を修正

jsx: title=gatsby-node.js(抜粋)
1const { paginate } = require("gatsby-awesome-pagination")
2
3... // 省略
4
5exports.createPages = async ({ graphql, actions }) => {
6  
7  ... // 省略
8
9  const result = await graphql(`
10    {
11      allMarkdownRemark(
12        sort: { order: DESC, fields: [frontmatter___date] }
13        limit: 1000
14      ) {
15        nodes {
16          id
17          fields {
18            slug
19          }
20        }
21      }
22    }
23  `)
24
25  ... // 省略
26
27  const posts = result.data.allMarkdownRemark.nodes
28
29  // ここで記事一覧を生成する
30  paginate({
31    createPage,
32    items: posts,
33    itemsPerPage: 10, // 10件ずつ表示する
34    pathPrefix: ({ pageNumber }) => (pageNumber === 0 ? "/" : "/page"), 
35    component: path.resolve(`./src/templates/index.js`),
36  })
37
38  ... // 省略
39
40

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

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

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

jsx: title=gatsby-node.js
1const path = require(`path`)
2const { createFilePath } = require(`gatsby-source-filesystem`)
3const { paginate } = require("gatsby-awesome-pagination")
4
5exports.onCreateNode = ({ node, getNode, actions }) => {
6  const { createNodeField } = actions
7  if (node.internal.type === `MarkdownRemark`) {
8    const value = createFilePath({ node, getNode, basePath: `pages` })
9    const url = node.frontmatter.path
10    createNodeField({
11      name: `slug`,
12      node,
13      value: url ? url : value,
14    })
15  }
16}
17
18exports.createPages = async ({ graphql, actions }) => {
19  const { createPage } = actions
20
21  const result = await graphql(`
22    {
23      allMarkdownRemark(
24        sort: { order: DESC, fields: [frontmatter___date] }
25        limit: 1000
26      ) {
27        nodes {
28          id
29          fields {
30            slug
31          }
32        }
33      }
34    }
35  `)
36
37  if (result.errors) {
38    throw new Error(result.errors)
39  }
40
41  const posts = result.data.allMarkdownRemark.nodes
42
43  // ここで記事一覧を生成する
44  paginate({
45    createPage,
46    items: posts,
47    itemsPerPage: 10,
48    pathPrefix: ({ pageNumber }) => (pageNumber === 0 ? "/" : "/page"),
49    component: path.resolve(`./src/templates/index.js`),
50  })
51
52  // ここで記事単体を生成する
53  posts.forEach(post => {
54    createPage({
55      path: post.fields.slug,
56      component: path.resolve(`./src/templates/article.js`),
57      context: {
58        slug: post.fields.slug,
59      },
60    })
61  })
62}
63

index.js を修正

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

props に pageContext を追加

jsx: title=src/templates/index.js(抜粋)
1const IndexPage = ({ data, location, pageContext }) => {
2  return (
3    ... ///省略
4  )
5});
6
7

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

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

クエリ部分を修正

jsx: title=src/templates/index.js(抜粋)
1
2... // 省略
3
4export const query = graphql`
5  query($limit: Int!, $skip: Int!) { //引数 $limit, $skip を追加
6    allMarkdownRemark(
7      sort: { fields: [frontmatter___date], order: DESC }
8      filter: { frontmatter: { type: { ne: "article" } } }
9      limit: $limit // 追加
10      skip: $skip // 追加
11    ) {
12      totalCount
13      edges {
14        node {
15          frontmatter {
16            title
17            year: date(formatString: "YYYY")
18            daymonth: date(formatString: "MM/DD")
19            categories
20            tags
21          }
22          fields {
23            slug
24          }
25          excerpt
26        }
27      }
28    }
29  }
30`
31

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

ページャーを追加

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

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

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

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

/ 以上

よかったらシェアしてください!