🔥

【Next.js】前後の記事リンクを出力を考察してみる その2

2022/05/29に公開

概要

前回、前後記事へのリンクをlocalStorageを使用して試してみました。

https://zenn.dev/kiriyama/articles/ddf308d9d46fb7

今度は、詳細ページの([id].jsなど)getStaticPropsgetServerSidePropsで前後の記事へのリンクを出力してみようかと思います。
割とこの方法はメジャー??なのかページ下部にある参考サイトでも似たような方法が書かれていたので、メジャーな方法かもですね。

最終目的

今回の最終目標は以下とする。
前回の記事でも同じですが今回はgetStaticPropsgetServerSidePropsでの処理となる。

  • 「前の記事」と「次の記事」へのリンクができるようにする
  • 最初の記事と最後の記事にきたら、ボタンを非表示にする

実際の流れ

では、実際の流れをメモしていく。
作業内容は以下。

  • 作業ファイルは[id].jsなどの詳細ページ
  • JSONファイルはjsonplaceholderのpostsデータ

JSONファイルは以下のデータを使用。
https://jsonplaceholder.typicode.com/posts

{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
},

詳細ページのgetServerSidePropsでの処理

詳細ページ([id].js)のgetServerSidePropsで処理は下記。
getServerSidePropsの部分のみ抜粋。

posts/[id].js

export const getServerSideProps = async (context) => {
  //現在のIDを取得 contextからidを取得する
  const currentId = Number(context.params.id);

  //全ての記事を取得する
  const res = await fetch(`https://jsonplaceholder.typicode.com/posts`);
  const allPosts = await res.json();
  const maxPageNumber = allPosts.length;

  //現在の投稿データを取り出す
  const currentPost = allPosts.find((post) => post.id === currentId);

  //現在のページの前後の記事を取得
  const prevPost = currentPost.id - 2 < 0 ? null : allPosts[currentPost.id - 2];
  const nextPost = currentPost.id === maxPageNumber ? null : allPosts[currentPost.id];

//現在のページの前後の記事を取得
  return {
    props: {
      currentPost,
      prevPost,
      nextPost,
    },
  };
};

現在のIDを取得 contextからidを取得する

これまで、contextから現在のid番号を取得していましが、このid番号は文字列型(string)となる。後ほどこのid番号をもとに配列からデータを取ってくる必要などある為、数値として扱いたいのでNumber()で型を変換している。

全ての記事を取得する

通常、各記事の詳細が欲しいので、fetch()では各記事のURLを指定していたが、今回はいったん記事一覧のURLを指定して記事全体の情報を取得してallPosts変数に入れる。
そのデータから記事の総数をmaxPageNumberという変数に入れてる。

現在の投稿データを取り出す

記事全体の情報を取得したが、現在の記事idの情報も取得しないといけないので、javascriptのfilter関数で各記事のidと現在の記事のidがイコール(===)なる記事情報を記事全体(allPosts)から現在の記事情報をcurrentPostに入れる。

現在のページの前後の記事を取得

前後の記事を取得するために参考演算子を使用している。

この理由としては、例えば現在の記事が「最初の記事」のidだった場合に前の記事のidはundefinedとなる。以下のエラーを見ていただく分かるが、nullを使ってくださいとなってる。

 Error: Error serializing `.prevPost` returned from `getServerSideProps` in "/posts/[id]".
Reason: `undefined` cannot be serialized as JSON. Please use `null` or omit this value.

その為、参考演算子で以下の条件が成立(true)となった場合はnullを代入するようにしている。

  • 前の記事のデータの場合は「前の記事のidが0より下回ったらnullを入れる」という条件にしている。
  • 次の記事のデータの場合は「次の記事のidが記事総数と同じだったらnullを入れる」という条件にしている。

前の記事の情報を取得するのに「-2」を設定している理由

前の記事も次の記事もそうだが、現在の記事idの前の記事を取り出すなら以下のように「-1」を指定しそうになる。

allPosts[currentPost.id - 1];

ではなぜ「-2」を指定しているのかというと「現在の記事id:4だった場合、配列のインデックスは0から始まるので、4番目のインデックスではid:5の記事となる。記事id:3のデータが欲しいので-2を指定することで現在の記事id:4の前の記事である記事id:3のデータが取得できる。」

といったように配列のインデックスと記事idがずれる。
今回は、現在の記事idを配列のインデックスを指定してるので、このようになってるいるのでそこは便宜注意する必要がある。

現在の記事、前の記事、次の記事のデータをそれぞれpropsに設定する

これで以下のようにデータが取得できたので、このデータをpropsに渡してあげて、実際にデータを使えるようにする。

  • 現在の記事(currentPost
  • 前の記事(prevPost
  • 次の記事(nextPost

前後のリンクボタンを作成する

getServerSidePropsからデータを取得できたら、リンクボタンを作成する。
など現在の記事の情報を表示するのは省略。

posts/[id].js
export default function Post({ currentPost, prevPost, nextPost }) {
  return (
    <div className="wrapper">
      // 現在の記事に関しては、currentPost.titleなどで表示する~
        <nav>
          {prevPost && (
            <Link href={`/posts/${prevPost.id}`}>
              <a>前の記事({prevPost.title}</a>
            </Link>
          )}
          {nextPost && (
            <Link href={`/posts/${nextPost.id}`}>
              <a>次の記事({nextPost.title}</a>
            </Link>
          )}
        </nav>
    </div>
  );
}

&&演算子を使って、prevPostnextPostにデータがある場合(trueの場合)はボタンを表示。
逆にprevPostnextPostnullの場合、、、つまり前の記事と次の記事が存在しない場合は条件が成立しないので、falseとなりボタンは表示されない。

まとめ

前回はlocalStorageによる前後のリンクボタンを作成してみたが、方法としてはこちらの方がいい。
というのも上記のコードを見ると分かるが、前後の記事データを取得できてるので、前後のボタンの表示名をそれぞれ「記事のタイトル」にしたい場合なども考慮すると、データを渡してあげたほうがいいような気がする。

参考サイト

https://qiita.com/ssaitho/items/4696820638d0e82844c8
https://kakioku.com/posts/2103040
https://blog.hpfull.jp/nextjs-microcms-pagination/
https://blog.microcms.io/next-pagination/

Discussion