【Next.js】前後の記事リンクを出力を考察してみる その2
概要
前回、前後記事へのリンクをlocalStorageを使用して試してみました。
今度は、詳細ページの([id].js
など)getStaticProps
やgetServerSideProps
で前後の記事へのリンクを出力してみようかと思います。
割とこの方法はメジャー??なのかページ下部にある参考サイトでも似たような方法が書かれていたので、メジャーな方法かもですね。
最終目的
今回の最終目標は以下とする。
前回の記事でも同じですが今回はgetStaticProps
やgetServerSideProps
での処理となる。
- 「前の記事」と「次の記事」へのリンクができるようにする
- 最初の記事と最後の記事にきたら、ボタンを非表示にする
実際の流れ
では、実際の流れをメモしていく。
作業内容は以下。
- 作業ファイルは[id].jsなどの詳細ページ
- JSONファイルはjsonplaceholderのpostsデータ
JSONファイルは以下のデータを使用。
{
"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
の部分のみ抜粋。
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
からデータを取得できたら、リンクボタンを作成する。
など現在の記事の情報を表示するのは省略。
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>
);
}
&&演算子を使って、prevPost
とnextPost
にデータがある場合(trueの場合)はボタンを表示。
逆にprevPost
とnextPost
がnull
の場合、、、つまり前の記事と次の記事が存在しない場合は条件が成立しないので、false
となりボタンは表示されない。
まとめ
前回はlocalStorageによる前後のリンクボタンを作成してみたが、方法としてはこちらの方がいい。
というのも上記のコードを見ると分かるが、前後の記事データを取得できてるので、前後のボタンの表示名をそれぞれ「記事のタイトル」にしたい場合なども考慮すると、データを渡してあげたほうがいいような気がする。
参考サイト
Discussion