その5 Dynamic Routes
5-1. Dynamic Routes
- 個々のブログページのURLはブログのデータに依存するようにしたいので、動的なルートを使用する
- 任意のページのURLがデータに依存する場合は、
dynamic routes
を導入する
- 任意のページのURLがデータに依存する場合は、
学べること
getStaticPaths
を使用してdynamic routes
でページを静的に生成する方法(5-3, 5-4)- ブログ投稿ごとにデータを取得するための
getStaticProps
の書き方(5-5) - remarkを使用してマークダウンをレンダリングする方法(5-6)
- 日付文字列をきれいに表示する方法(5-7)
- 動的ルートを使用してページにリンクする方法(5-8)
- 動的ルートに関するいくつかの有用な情報(5-9)
5-2. Steup
5-3. Page Path Depends on External Data
- 前回のレッスンでは、ページのコンテンツが外部データに依存するケースを取り上げました。
インデックスページをレンダリングするために必要なデータをフェッチするために、getStaticPropsを使用しました。
このレッスンでは、各ページのパスが外部データに依存している場合について説明します。
- Next.jsでは、外部データに依存するパスをもつページを静的に生成することができます。これによりNext.jsでは動的URLの利用が可能になります。
外部データに依存するページパス
- 外部データ(データベースなど)に依存するページをプリレンダリングできる
- 外部ページから
id
を引っ張り出して、/posts/[id]
のようなページを生成する
ダイナミックルートでページを静的に生成する方法
- ブログ投稿のための動的なルートを作成したい
ステップの概要
- 以下のステップを踏むことで実現できます。まだ変更する必要はありません。次のページですべて行います。
- まず、
pages/postsの下に[id].js
というページを作成する(Next.jsでは、[
で始まり]
で終わるページがダイナミックルートになる) pages/posts/[id].js
には、これまで作成した他のページと同じように、投稿ページをレンダリングするコードを記述します。
import Layout from '../../components/layout'; export default function Post() { return <Layout>...</Layout>; }
- さて、ここからが新機能です。
- このページから
getStaticPaths
という非同期関数をエクスポートします。 - この関数では、
id
に対して取り得る値のリストを返す必要があります。
import Layout from '../../components/layout'; export default function Post() { return <Layout>...</Layout>; } export async function getStaticPaths() { // Return a list of possible value for id }
- 最後に、
getStaticProps
を再び実装する必要があります - 今回は、与えられたidを持つブログ記事に必要なデータを取得します。
import Layout from '../../components/layout'; export default function Post() { return <Layout>...</Layout>; } export async function getStaticPaths() { // Return a list of possible value for id } export async function getStaticProps({ params }) { // Fetch necessary data for the blog post using params.id }
[まとめ]
/posts/<id>
というパスを持つページを静的に生成する(<id>
はファイル名から決定され、これを動的と呼んでいる?)- 具体的には、
/pages/posts/[id].js
というページを作り、下記の内容を含める([
と]
がNext.jsにおいて動的ページであることを示している)- このページを描画するためのReact要素
id
に基づく配列を返すgetStaticPaths
関数id
を使用して、ブログの投稿データを取得するgetStaticProps
関数
- 具体的には、
5-4. Implement getStaticPaths
getStaticPathsの実装
- まず、ファイルを設定しましょう。
pages/posts
ディレクトリの中に[id].js
というファイルを作成します。- また、pages/postsディレクトリ内のfirst-post.jsを削除してください。これはもう使いません。
- そして、pages/posts/[id].jsをエディタで開き、以下のコードを貼り付けます。後で...を記入します。
import Layout from '../../components/layout'; export default function Post() { return <Layout>...</Layout>; }
- 次に、lib/posts.jsを開き、以下のgetAllPostIds関数を一番下に追加します。
- これは、postsディレクトリにあるファイル名(.mdを除く)のリストを返します。
export function getAllPostIds() { const fileNames = fs.readdirSync(postsDirectory); // Returns an array that looks like this: // [ // { // params: { // id: 'ssg-ssr' // } // }, // { // params: { // id: 'pre-rendering' // } // } // ] return fileNames.map((fileName) => { return { params: { id: fileName.replace(/\.md$/, ''), }, }; }); }
重要: 返されるリストは単なる文字列の配列ではなく、上のコメントのようなオブジェクトの配列である必要があります。
- 各オブジェクトはparams
キーを持ち、id
キーを持つオブジェクトを含んでいなければなりません (ファイル名に[id]を使っているからです)。
- そうでないと、getStaticPaths
は失敗します。
- 最後に、getAllPostIds関数をインポートして、getStaticPathsの内部で使用することにします。
pages/posts/[id].js
を開き、エクスポートしたPostコンポーネントの上に以下のコードをコピーしてください。
import { getAllPostIds } from '../../lib/posts'; export async function getStaticPaths() { const paths = getAllPostIds(); return { paths, fallback: false, }; }
- paths は、getAllPostIds() が返す既知のパスの配列を含みます。
- この配列には pages/posts/[id].js で定義されたパラメータが含まれます。詳しくはpathsキーのドキュメントをご覧ください。
- fallback: false を無視する - 後で説明します。
- これでほぼ完成です。しかし、まだgetStaticPropsを実装する必要があります。
次のページでそれをやってみましょう。
5-5. Implement getStaticProps
getStaticPropsの実装 - 指定されたidの投稿をレンダリングするために、必要なデータを取得する必要があります。 - そのためには、lib/posts.jsをもう一度開き、以下のgetPostData関数を一番下に追加します。これは、idに基づいた投稿データを返します。
export function getPostData(id) { const fullPath = path.join(postsDirectory, `${id}.md`); const fileContents = fs.readFileSync(fullPath, 'utf8'); // Use gray-matter to parse the post metadata section const matterResult = matter(fileContents); // Combine the data with the id return { id, ...matterResult.data, }; }
- 次に、pages/posts/[id].jsを開き、この行を置き換えます。
import { getAllPostIds } from '../../lib/posts';
を以下のコードで実行します。
import { getAllPostIds, getPostData } from '../../lib/posts'; export async function getStaticProps({ params }) { const postData = getPostData(params.id); return { props: { postData, }, }; }
- 投稿ページでは、getStaticPropsのgetPostData関数を使って、投稿データを取得し、propsとして返すようになりました。
- では、Postコンポーネントを更新してpostDataを使えるようにしましょう。
- pages/posts/[id].jsで、エクスポートしたPostコンポーネントを以下のコードに置き換えてください。
export default function Post({ postData }) { return ( <Layout> {postData.title} <br /> {postData.id} <br /> {postData.date} </Layout> ); }
- 以上です。これらのページを見てみてください。
- http://localhost:3000/posts/ssg-ssr
- http://localhost:3000/posts/pre-rendering
- 各ページのブログデータを見ることができるはずです。
素晴らしい!ダイナミックルートの生成に成功しました。 何か問題がありますか? もしエラーに遭遇したら、ファイルに正しいコードがあることを確認してください。 pages/posts/[id].js はこのようになっているはずです。 lib/posts.jsはこのようになっているはずです。 (それでも動かない場合) 残りのコードは以下のようになっているはずです。 それでもうまくいかない場合は、GitHub Discussionsでコミュニティに質問してください。GitHub にコードをプッシュして、他の人が見られるようにリンクを張ってくれると助かります。 まとめ 繰り返しになりますが、私たちが行ったことをグラフィカルにまとめると、以下のようになります。 動的ルートを持つページを静的に生成する方法 まだ、ブログのマークダウンコンテンツを表示していません。次はこれをやってみましょう。 ### 5-6. Render Markdown ### 5-7. Polishing the Post Page ### 5-8. Polishing the Index Page ### 5-9. Dynamic Routes Details