Next.jsのダイナミックルーティングを実装してみた

Next.jsでは、getStaticPaths・getStaticProps・getServerSidePropsといった似た名前の関数が多く違いがわかりづらいですよね...

今回は、その中でも使用頻度の高いgetStaticPaths・getStaticPropsを使用し、ダイナミックルーティングの簡単なサンプルを作成したいと思います。

こちらが今回作成するサンプルの完成形です。
![画面収録-2020-10-15-17.32.51.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584128/591210bb-99ff-8665-d3d4-2fe289a8e648.gif)
URL：https://nextjs-dynamic.vercel.app/



[JSONPlaceholder](https://jsonplaceholder.typicode.com/)のAPIサーバーを使用し、記事一覧・記事詳細ページを表示しています。
見ての通り、全てのデータが事前ビルドされているため表示が高速です。

では、実装までの流れについて説明していきます。

#1. ダイナミックルート用のファイル作成
まずは、ダイナミックルート用のファイル（記事詳細ページ）を作成します。
Next.jsでは、pages内でファイル名に`[]`を使用することで自動的にダイナミックルート対象となります。

```
 /pages/[id].js                               
   ├ http://localhost:3000/1
   ├ http://localhost:3000/2
   └ ...
```

ダイナミックルート用のファイルでは、`getStaticPaths`と`getStaticProps`の関数が必要です。

**getStaticPaths**：ビルド時にレンダリングする必要のあるパスのリストを生成する
**getStaticProps**：ビルド時に静的なファイルを生成し、ページコンポーネントで使用する値を用意する

これらの関数はクライアント側で実行されることはなく、**必ずサーバーサイドで実行**されます。

以下は作成した記事詳細ページです。

```react:pages/[id].js
import Link from 'next/link'

// post：getStaticPropsから取得したデータ
export default ({ post }) => {
  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.body}</p>
      <Link href="/">
        <a>Back</a>
      </Link>
    </div>
  )
}

export const getStaticPaths = async () => {
  // 外部APIエンドポイントを呼び出しデータ取得
  const res = await fetch("https://jsonplaceholder.typicode.com/posts")
  const posts = await res.json()  

  // 事前ビルドしたいパスを指定
  const paths = posts.map((post) => ({
    params: {
      // ファイル名と合わせる ※文字列指定
      id: post.id.toString(),
    },
  }))
  // paths：事前ビルドするパス対象を指定するパラメータ
  // fallback：事前ビルドしたパス以外にアクセスしたときのパラメータ true:カスタム404Pageを表示 false:404pageを表示
  return { paths, fallback: false }
}

// paramsには上記pathsで指定した値が入る（1postずつ）
export const getStaticProps = async ({ params }) => {  
  // 外部APIエンドポイントを呼び出しデータ取得
  const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${params.id}`)
  const post = await res.json()  

  // ページコンポーネントにpropsとしてに渡す
  return {
    props: {
      post
    },
  }
}
```

###画面表示までの流れ
1. getStaticPathsで、レンダリングする必要のあるパスのリストを生成する
2. getStaticPropsで、1で生成したパスのリストから1postsずつAPIエンドポイントを呼び出しデータを取得する
4. ページコンポーネントがgetStaticPropsからデータを受け取り、画面に表示される

実際にURLにアクセスしてみると、表示が切り替わることが確認できるかと思います。

![画面収録-2020-10-15-16.30.12.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584128/400f9ae0-40e7-4b12-9aba-4dcb083aa820.gif)


#2. 記事一覧ページ作成
次に、それぞれの詳細ページにリンクする記事一覧を作成します。

こちらのページに関しても、`getStaticProps`でページコンポーネントで使用する値を事前に用意する必要があります。

以下は作成した記事一覧ページです。

```react:pages/index.js
import Link from 'next/link'

// posts：getStaticPropsで取得したデータを受け取る
const Home = ({ posts }) => {
  return (
    <div>
      <h1>記事一覧</h1>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>
            {/* リンク先を指定 */}
            <Link href={`/${post.id}`}>
              <a>{post.title}</a>
            </Link>
          </li>
        ))}
      </ul>
    </div>
  )
}

// ビルド時にデータを取得し静的なファイルを事前に生成
export const getStaticProps = async () => {
  // 全記事データを取得
  const res = await fetch("https://jsonplaceholder.typicode.com/posts")
  const posts = await res.json()  

  // コンポーネントに渡すデータを指定
  return {
    props: {
      posts,
    },
  }
}

export default Home
```

###画面表示までの流れ
1. getStaticPropsで、画面表示させたいデータを取得する
2. ページコンポーネントがgetStaticPropsからデータを受け取り、画面に表示される

以上でダイナミックルーティングの実装完了になります！

#おわりに
Next.js公式がSSGを推薦していることもあり、今後さらにダイナミックルーティングを使用する機会が増えていきそうですね！

公式チュートリアルでは、実際にブログを作成しながらダイナミックルーティングについて学ぶことができるので、より詳しく知りたいという方はぜひお試し下さい！
[Next.js Dynamic Routes](https://nextjs.org/learn/basics/dynamic-routes)


#参考資料
[Next.js 公式ドキュメント](https://nextjs.org/docs/getting-started)
[Next.js 9.3 Released, Improves Static Site Generation](https://www.infoq.com/jp/news/2020/09/nextjs-93-ssg-sass-preview/)
[JSONPlaceholder](https://jsonplaceholder.typicode.com/)