見出し画像

Gatsbyサイトにページネーションを追加する

ブログの記事がだんだん増えてくると、1つのページ内に全ての記事を表示することが困難になってきます。ページを表示する時間が遅くなったり、視認性が悪くなったりもします。

そうした場合にページを分割する方法を知っておくと、一覧をみやすくすることができます。

商品がたくさんある場合に、1ページに表示する商品数を制限したりすることにも使えます。

今回は、ページネーションをGatsbyで実装する方法について説明していきます。

Gatsbyにページネーションを追加する

前回の記事で作成したカテゴリーページのブログ記事を、2記事ずつ表示するように変更してみましょう。

カテゴリーページに表示されるブログ記事の総数を、各ページに表示する記事の数で割り、作成する必要があるページ数を計算しておきます。

後は、各ページでページ数を参照して、そのページ数に該当するブログ記事をGraphQLでクエリーするようにすれば良いわけですね。

カテゴリーを作成した時と同じように、ページネーションで使う各ページもgatsby-node.jsでページを作成していきます。

各カテゴリーにあるブログ記事の数を数えるには、gatsby-node.jsのGraphQLのクエリに、totalCountを追加するだけで済みます。

  const result = await graphql(`
   {
     categoryGroup: allMdx{
       group(field: frontmatter___category) {
         fieldValue
         totalCount
       }
     }
   }
 `)

このtotalCountを利用して、各カテゴリーに必要なページ数を計算します。

   const postsPerPage = 2
   const numPages = Math.ceil(category.totalCount / postsPerPage)

categories.forEachの中で、各カテゴリーのページを作成していましたね。このforEachの中にさらにループを追加して、分割したページを作成するようにします。

const path = require("path")

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

 const pagenationTemplate = path.resolve("src/templates/category.js")

 const result = await graphql(`
   {
     categoryGroup: allMdx {
       group(field: frontmatter___category) {
         fieldValue
         totalCount
       }
     }
   }
 `)

 if (result.errors) {
   reporter.panicOnBuild(`Error while running GraphQL query.`)
   return
 }

 const categories = result.data.categoryGroup.group

 categories.forEach(category => {

   const postsPerPage = 2
   const numPages = Math.ceil(category.totalCount / postsPerPage)

   Array.from({ length: numPages }).forEach((_, i ) => {
       const page_path = i === 0 ? `/` : `/${i + 1}`
       createPage({
           path: `/blogs/categories/${category.fieldValue}${page_path}`,
           component: pagenationTemplate,
           context: {
               category:category.fieldValue,
               limit: postsPerPage,
               skip: i * postsPerPage,
               numPages,
               currentPage: i + 1,
           }
       })
   })


 })
}

カテゴリーの中に含まれる記事数に応じて、ページを作成するようにしました。作成されたページを見てみると、blogs/categories/カテゴリ名の下に、数字のついたページが作成されていますね。

スクリーンショット 2021-11-14 12.20.38

ただ、これだと効果がわかりにくいので、categoryが"マーケティング"の記事を2つ、"開発"の記事を2つ追加してみましょう。

スクリーンショット 2021-11-14 12.24.29

マーケティングと開発のページが想定通り増えました。

Gatsbyのブログ記事をページ数によってわける

これらのページを開いてみると、まだ全ての記事が表示されるようになっています。

テンプレートであるcategory.jsの中で、該当するブログ記事を表示するようにしなければなりません。そのために、gatsby-node.jsのcontextの中で、さまざまな値をcategory.jsに渡すようにしています。

まずはGraphQLのクエリーを変更しましょう。"skip"でそれまでのページまでのブログ記事を飛ばし、"limit"で次に表示するブログ記事の数を制限しています。

例えば、今回は2記事毎にページを作成しているので、2ページ目では最初の2記事を飛ばして次の2記事を表示するようになります。

export const pageQuery = graphql`
 query($category: String, $skip: Int!, $limit: Int!) {
   allMdx(
     limit: $limit
     skip: $skip
     sort: { fields: [frontmatter___date], order: DESC }
     filter: { frontmatter: { category: { in: [$category] } } }
   ) {
     totalCount
     edges {
       node {
         slug
         frontmatter {
           title
         }
       }
     }
   }
 }
`

続いてカテゴリーのページ間を、遷移できるリンクを追加します。

pageContextから、新たにcurrentPageとnumPagesを入手します

const { category, currentPage, numPages } = pageContext

これらの値を利用して、現在のページが、最初や最後のページかどうか判定できるようにしています。これに応じて、"前のページ"へのリンクや”次のページ”のリンクが表示されるようにします。

const isFirstPage = currentPage === 1
const isLastPage = currentPage === numPages
const previousPage = currentPage - 1 === 1 ? `` : (currentPage - 1).toString()
const nextPage = (currentPage + 1).toString()
const previousPageLink = `/blogs/categories/${category}/` + previousPage
const nextPageLink = `/blogs/categories/${category}/` + nextPage

後は好きな場所に、このリンクを追加するだけです。

最初のページの場合、"前のページ"へのリンクを表示しないように。最後のページの場合、"次のページ"へのリンクを表示しないようにします。

<div>
    {!isFirstPage && (
     <Link to={previousPageLink} rel="prev">
       ← 前のページへ
     </Link>
    )}
    {!isLastPage && (
     <Link to={nextPageLink} rel="next">
       次のページへ →
     </Link>
    )}
</div>
     

これでページを移動することができるようになりました。

最初のページ

http://localhost:8000/blogs/categories/マーケティング/

スクリーンショット 2021-11-14 12.34.01

2番目のページ

http://localhost:8000/blogs/categories/マーケティング/2

スクリーンショット 2021-11-14 12.34.56

3番目のページ

http://localhost:8000/blogs/categories/マーケティング/3

スクリーンショット 2021-11-14 12.35.10

ソースコード全体

# src/pages/templates/category.js

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

const Categories = ({ pageContext, data }) => {

 const { category, currentPage, numPages } = pageContext
 const { edges, totalCount } = data.allMdx
 
 const categoryHeader = `[${category}]カテゴリーで${totalCount}個の記事が見つかりました。`
 
 const isFirstPage = currentPage === 1
 const isLastPage = currentPage === numPages
 const previousPage = currentPage - 1 === 1 ? `` : (currentPage - 1).toString()
 const nextPage = (currentPage + 1).toString()
 
 const previousPageLink = `/blogs/categories/${category}/` + previousPage
 const nextPageLink = `/blogs/categories/${category}/` + nextPage
 
 return (
   <div>
     <h1>{categoryHeader}</h1>
     <ul>
       {edges.map(({ node }) => {
         const { slug } = node
         const { title } = node.frontmatter
         return (
           <li key={slug}>
             <Link to={"/blogs/"+slug}>{title}</Link>
           </li>
         )
       })}
     </ul>
     <Link to="/blogs/categories/">カテゴリーページに戻る</Link>

     <div>
       {!isFirstPage && (
         <Link to={previousPageLink} rel="prev">
           ← 前のページへ
         </Link>
       )}
       {!isLastPage && (
         <Link to={nextPageLink} rel="next">
           次のページへ →
         </Link>
       )}
     </div>
     
   </div>
 )
}

export default Categories

export const pageQuery = graphql`
 query($category: String, $skip: Int!, $limit: Int!) {
   allMdx(
     limit: $limit
     skip: $skip
     sort: { fields: [frontmatter___date], order: DESC }
     filter: { frontmatter: { category: { in: [$category] } } }
   ) {
     totalCount
     edges {
       node {
         slug
         frontmatter {
           title
         }
       }
     }
   }
 }
`

実行結果

まとめ

カテゴリーページを少し改良するだけで、ページネーションを追加することができました。


ここまで読んでいただけたなら、”スキ”ボタンを押していただけると励みになります!(*´ー`*)ワクワク

この記事が気に入ったらサポートをしてみませんか?