本サイトは gatsby-starter-default をベースに構築している。そのためページネーションのような気の利いた機能は元からついていない。そこまで多くの記事を書く見通しは経っていないが、記事一覧が延々と縦に長くなっていくのはどうにも落ち着かないので早々にページネーション機能は整えておきたかった。
ページネーションの自作
ページネーションみたいな世の中一般的な機能がプラグインとして存在しないわけはない。それは疑うまでもないことだが、チュートリアルをこなし Gatsby の拡張方法がなんとなくわかってきたこのタイミングをみすみす逃すよりはこのくらいの機能であれば自作することでより Gatsby と親密になれるだろうと思った。
何より「こうやったら実現できそう」という設計が頭の中にあったので試さずにはいられなかった。
大まかに必要な事は下記
- ページネーションの各ページ用のレイアウトファイルを作成する。
gatsby-node.jsでページネーションの各ページ用の URL を作成する。
ページネーション用のレイアウト
レイアウトファイルは下記のようにした。「ページネーションの数字がどのように切り替わるのか」というロジックは MaterialUI 先生に丸投げした。そのため PostsPagination コンポーネントは @material-ui/lab/Pagination の Pagination コンポーネントを薄くラップしてあるだけだ。PostExcerpt は記事見出しのコンポーネント。
src/layouts/pagination-layout.js
1import React from "react"2import { graphql } from "gatsby"34import GlobalLayout from "./global-layout"5import SEO from "../components/seo"6import PostsPagination from "../components/posts-pagination"7import PostExcerpt from "../components/post-excerpt"89export default function PaginationLayout ({ data, pageContext }) {10 const {pageNum, lastPageNum} = pageContext11 return (12 <GlobalLayout>13 <SEO title="All Posts" />14 {data.allFile.edges.map(({node}, idx) => {15 return (16 <PostExcerpt17 key={idx}18 createdDate={node.childMdx.frontmatter.date}19 name={node.name}20 title={node.childMdx.frontmatter.title}21 excerpt={node.childMdx.excerpt}22 />23 )24 })}25 <PostsPagination currentPageNum={pageNum} lastPageNum={lastPageNum} />26 </GlobalLayout>27 );28}2930export const query = graphql`31query PaginationLayoutQuery($maxPostsInPage: Int, $skipCount: Int) {32 allFile(33 filter: {sourceInstanceName: {eq: "posts"}, internal: {mediaType: {eq: "text/mdx"}}}34 limit: $maxPostsInPage35 skip: $skipCount36 sort: {fields: childMdx___frontmatter___date, order: DESC}37 ) {38 edges {39 node {40 name41 childMdx {42 frontmatter {43 title44 date45 }46 excerpt47 }48 }49 }50 }51}52`
ページネーションの各ページ
レイアウトができたので、ページネーションの各ページの URL を作る。一部省略だが gatsby-node.js を下記のように編集した。こうすることで /page/1/ や /page/2/ のような URL が Gatsby に登録される。
gatsby-node.js
1exports.createPages = async ({ graphql, actions }) => {2 const { createPage } = actions3 await createPaginationPages(graphql, createPage)4}56async function createPaginationPages(graphql, createPage) {7 const result = await graphql(`8 {9 allMdx {10 totalCount11 }12 site {13 siteMetadata {14 maxPostsInPage15 }16 }17 }18 `)19 const totalPostsCount = result.data.allMdx.totalCount20 const maxPostsInPage = result.data.site.siteMetadata.maxPostsInPage21 const lastPageNum = Math.ceil(totalPostsCount/maxPostsInPage)22 for (let start = 1; start <= lastPageNum; start++) {23 const slug = `/page/${start}/`24 createPage({25 path: slug,26 component: path.resolve(`./src/layouts/pagination-layout.js`),27 context: {28 pageNum: start,29 maxPostsInPage: maxPostsInPage,30 lastPageNum: lastPageNum,31 skipCount: (start - 1) * maxPostsInPage,32 }33 })34 }35}