microCMS

Next.js(SSG)でページネーションを実装してみよう

エンジニアリング
かみむら

こんにちはかみむらです。

本記事ではNext.js(SSG)でページネーションを実装していきます。以前に書いたmicroCMS + Next.jsでJamstackブログを作ってみようの拡張版になります。本記事はこのチュートリアルを終えてる前提で話を進めていきます。

今回の作成イメージはmicroCMSブログのコンテンツ一覧の下部にあるページネーションです。

前提

下記のバージョンで開発を行っています。バージョンの差異によって若干機能が異なる可能性があります。

  • Next 10.0.1
  • react 17.0.1
  • react-dom 17.0.1


コンポーネントの作成

はじめに、ページネーションを作成するためのベースとなるコンポーネントを作成します。components/Pagination.jsを作成してください。

こちらはコードの全体像です。

//components/Pagination.js
import Router from 'next/router';
import Link from 'next/link';

export const Pagination = ({ totalCount }) => {
  const PER_PAGE = 5;

  const range = (start, end) =>
        [...Array(end - start + 1)].map((_, i) => start + i)

  return (
    <ul>
      {range(1, Math.ceil(totalCount / PER_PAGE)).map((number, index) => (
        <li key={index}>
          <Link href={ `/blog/page/${number}`}>
            <a>{number}</a>
          </Link>
        </li>
      ))}
    </ul>
  );
};

このコンポーネントの説明は以下になります。

rangeの引数に(start, end)を与えて、その値を元に配列を作成します。これで、totalCountが20でPER_PAGEが5とすると(20 /5)で4が返ります。これはページネーションのリスト数になります。
② rangeで作成した配列をmapして各数値を取り出します。

実際にコンポーネントのtotalCountに数値を入れると以下のようになります。

<Pagination totalCount={20} />

実装通り4つのリストを作成することができました。

一覧ページでのページネーション

上記で作成したコンポーネントを使って、一覧ページにページネーションの実装をしていきましょう。

それでは、pages/index.jsにロジックを書いていきます。一覧ページの実装はmicroCMS + Next.jsでJamstackブログを作ってみようを参考にしています。こちらはコードの全体像です。

// pages/index.js
import Link from 'next/link';
import { Pagination } from '../components/Pagination';

export default function Home({ blog, totalCount }) {
  return (
    <div>
      <ul>
        {blog.map(blog => (
          <li key={blog.id}>
            <Link href={`blog/${blog.id}`}>
              <a>{blog.title}</a>
            </Link>
          </li>
        ))}
      </ul>
      <Pagination totalCount={totalCount} />
    </div>
  );
}

export const getStaticProps = async () => {
  const key = {
    headers: {'X-API-KEY': process.env.API_KEY},
  };
  const data = await fetch('https://your-service.microcms.io/api/v1/blog?offset=0&limit=5', key)
    .then(res => res.json())
    .catch(() => null);

  return {
    props: {
      blog: data.contents,
      totalCount: data.totalCount
    },
  };
};

ポイントはtotalCountでコンテンツの合計を取得してるところです。microCMSのリスト形式のレスポンスにはすべての合計値(totalCount)が含まれています。この値をpropsとして受け取って、Paginationに渡しています。

もう一つのポイントは、microCMSのAPIでoffsetlimitを使ってコンテンツ数を絞っています。

https://your-service.microcms.io/api/v1/blog?offset=0&limit=5

offsetは何件目から取得するかを指定します。デフォルト値は0です。
limitは取得件数を指定します。デフォルト値は10です。ここでは5件取得しています。

試しに、microCMSの記事数を10件作成して検証してみましょう。上から10~6件目のコンテンツが取得できます。

個別のページネーション

次に、絞り込みを行った個別のページを作成していきます。

SSGでページネーションを作成する場合は、SSRと違って/page/1/page/2のようにして、各ページ毎にコンテンツを取得するのがベターです。それでは、pages/blog/page/[id].jsを作成してください。コードの全体像はこちらです。

// pages/blog/page/[id].js
import Link from 'next/link';
import { Pagination } from '../../../components/Pagination';

const PER_PAGE = 5// pages/blog/[id].js
export default function BlogPageId({ blog, totalCount }) {
  return (
    <div>
      <ul>
        {blog.map(blog => (
          <li key={blog.id}>
            <Link href={`blog/${blog.id}`}>
              <a>{blog.title}</a>
            </Link>
          </li>
        ))}
      </ul>
      <Pagination totalCount={totalCount} />
    </div>
  );
}

// 動的なページを作成
export const getStaticPaths = async () => {
  const key = {
    headers: { 'X-API-KEY': process.env.API_KEY }
  };

  const res = await fetch('https://your-service.microcms.io/api/v1/blog', key)

  const repos = await res.json();

  const pageNumbers = [];

  const range = (start, end) =>
        [...Array(end - start + 1)].map((_, i) => start + i)

  const paths = range(1, Math.ceil(repos.totalCount / PER_PAGE)).map((repo) =>  `/blog/page/${repo}`)

  return { paths, fallback: false };
};

// データを取得
export const getStaticProps = async (context) => {
  const id = context.params.id;

  const key = {
    headers: { 'X-API-KEY': process.env.API_KEY }
  };

  const data = await fetch(
    `https://your-service.microcms.io/api/v1/blog?offset=${(id - 1) * 5}&limit=5`,
    key
  ).then(res => res.json()).catch(() => null)

  return {
    props: {
      blog: data.contents,
      totalCount: data.totalCount
    }
  };
};

少し冗長的なコードになりましたが重要なところを説明していきます。 

getStaticPathsで動的なページを作成

先程と同じで、range という関数を使って配列を作成します。PER_PAGEが5なのでtotalCountが20の場合は4つのページ(例: blog/page/1、blog/page/2...)が作成できます。

getStaticPropsでデータを取得

次にデータ取得部分を見てみましょう。ここではoffsetに注目してください。context.params.idでは[] (ブラケット)の値を取得することができます。このidの数値を元にして、offsetの値を決めます。例えば、blog/page/2の場合idが2になるので(2-1) * 5でoffset=5の状態を作ることができます。

https://your-servce.microcms.io/api/v1/blog?offset=${(id - 1) * 5}&limit=5`

開発サーバーを立ち上げて、下部にあるリストの2をクリックするとblog/page/2にアクセスできます。ここで5~1件目のデータが取得できました。

おわりに

今回はNext.jsのページネーションについて解説しました。ページネーションはWeb制作において重要なコンポーネントになります。ぜひ、この記事がお役に立てば嬉しいです。


-----

microCMSは日々改善を進めています。
ご意見・ご要望は管理画面右下のチャット、公式Twitterメールからお気軽にご連絡ください!
引き続きmicroCMSをよろしくお願いいたします!

ABOUT ME

かみむら
フロントエンドエンジニア。テックブロガーでもあります。JAMstackアーキテクチャーやSPA(React、Vue)技術が好きです。

microCMSとは

  1. 開発者、編集者どちらも分かりやすい管理画面

  2. 細かな権限管理や豊富な外部サービス・データ連携

  3. 安心の日本製・日本語でのチャットサポート