[Next.js 超初心者向け] 今度こそ動的ルーティングをわかろう!
前書き
皆さんはNext.jsを使用して開発している方が多いでしょう。僕のその一人で、昨年の7月にプログラミングを初めてから、今までNext.jsを使ってきました。そんな中で初学者がまずぶつかる難所に当たるのがこの「動的ルーティング」だと考えています。というわけで改めて動的ルーティング(Dynamic Route)とは何なのかみていきましょう。
そもそもなんなのか
動的ルーティングとは超ざっくりいうと状況に合わせて変化するURLを作ることを指します。
例えば、主にユーザーによって異なるページ(例:ユーザーマイページ)を表示したいときに使います。
皆さんが書く普段のコードと比較していきましょう。
例えば、普段皆さんが書くようなルーティングはこのようなパスで書かれていることでしょう。
export default function CatPage() {
return (
<h1>猫</h1>
);
}
この場合、どのような状況においても必ず、/animal/catと打ち込めば必ず上記のコードの内容が反映されます。このようなコードを動的ルーティングと対照的に静的ルーティングと呼びます。
対して、動的ルーティングの例をみていきましょう。
type Prop = {
params: { id: string }
}
export default async function Page({ params }):Prop {
return (
<div>
<h1>{ params.id }</h1>
</div>
);
}
いきなり訳分からん単語が出てきたなと思うかもしれませんが、とりあえずこれで状況に応じてページ変わるんだなと今は思っといてください。次で、このコードの解説を始めます。
コードの詳細
ここでは先ほどのコードを分解して、個々の意味を解説していきます。
-
[id]
これは動的、つまりは状況によってコロコロ変わる部分そのものに当たります。
例えば、上記コードのパスは /animal/[id] になっています。そしてこのパスの[id]の部分が
catになることもあれば(/animal/cat)、dogになることもある(/animal/dog)というわけです。
例えるなら[id]は空箱で、この箱には様々なものが入ってきます。その度に箱(urlパス)の名前が変わるというイメージです。そして、/[id]/page.tsxではその箱に入ることが想定されるもの(ブログだったら記事、snsだったらユーザーに関するもの)を整える目的で記述します。Twitter(新:X)ではどんなアカウントも左上にアイコンがあって、ヘッダー画像が上にあります。そのような「これはどんなユーザーでも一緒の部分だよね」ってものを書くのがここに当たります。 -
params
これは前のパスの情報を取得するために使うものです。試しにconsole.logでparamsの中身をみていきましょう。以下の画像ではパスを/animal/catにしてあります。
なんかごちゃごちゃ書いてありますね。その中に「id:'cat'」とあります。つまりこのparamsは[id]の中身をゲットしたということです。今回はこの大量の記述の中で[id]の情報だけ欲しいので{params.id}と記述することでパスの名前を取得できるというわけですね。
- type Prop = { params: { id: string } }
これは型を記述するTypeScriptの記法です。今回はパスを取ってくるものなので当然urlは文字列という理屈からstringになるというわけです。
- async(非同期処理)
Next.jsの最新バージョン15では動的ルーティングの記述に非同期関数を定義しないとコンソールエラーを吐き出すようになりました。これは、Next.js 15 で fetch() などの非同期データ取得処理をページレベルで扱いやすくするための仕様変更によるものです。
ここまでを踏まえてもう一度コードを見てましょう。
type Prop = {
params: { id: string }
}
export default async function Page({ params }):Prop {
return (
<div>
<h1>{ params.id }</h1>
</div>
);
}
さっき見た時よりコードの中身がわかってきたのではないでしょうか?
最後に動的ルーティングを活かした応用編をちょっと紹介していきたいと思います。
動的ルーティング 応用編
先ほどはidのパスを取得するだけでしたが、今度は名前や画像なども動的に処理してみましょう。
type Prop = {
params: { id: string };
};
const animalData: Record<string, { name: string; image: string }> = {
dog: { name: "犬", image: "/dog.png" },
cat: { name: "猫", image: "/cat.png" },
bird: { name: "鳥", image: "/bird.png" },
};
export default function Page({ params }: Prop) {
const animal = animalData[params.id] || { name: "不明な動物", image: "/unknown.png" };
return (
<div>
<h1>動物の紹介</h1>
<h2>{animal.name}</h2>
<img src={animal.image} alt={animal.name}/>
</div>
);
}
解説すると、まずanimalDataで動物の名前と画像のデータを定義します。こうすることで例えば、animalData[dog]の場合、dog: { name: "犬", image: "/dog.png" }の内容が入っている状態になっているということですね。
そして、Record<string, { name: string; image: string }>というTSのユーティリティ型で、string をキーに、統一された型のオブジェクトを作ります。これでidと型を合わせて使いやすくしています。そしてanimalData[params.id]によって書かれたパスをDataを照らし合わして動的に表示します。これによって動物のページに合わせた名前と画像を持ってくることができます。
おわりに
これは動的ルーティングの先っちょの先っちょです。この機能をAPIや様々なものと組み合わせることでブログやSNSなどいろんな可能性の幅が広がっていきます。しかし今回説明した基本概念は応用を凝らしても一貫しています。工夫を凝らしまくってちょっと基本的なことが混乱してきたなと思ったらまた読み返してみてください。
Discussion