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