Next.js 公式ドキュメントで Page Router を学ぶ

はじめに
Next.jsでアプリケーションの開発することになったが、いきなりプロダクションコードでApp routerを使うのは怖かったので、Page routerからやることにしました。

レンダリング
概要
- デフォルトでは、Next.jsはサーバーサイドでHTMLを事前にレンダリングする(プリレンダリング)
- サーバーサイドでレンタリングされたHTMLに対して、ブラウザのJavaScriptを組み込む(ハイドレーション) → 例えば、クリックイベントなど。
- プリレンダリングの種類は、SSRとSSG
SSR
- ページのリクエストごとに、HTMLが生成される
- サーバーサイドレンダリングされるタイミングで、APIを叩きたい場合 getServerSideProps を使う。
SSG
- 静的生成して、アプリケーションを表示させる
- APIを叩きたい場合 getStaticProps を使う(ビルド時に叩く感じになる。データが古い)
- パスがAPIに依存する場合([id].tsxなど)の時、パスの取得を
getStaticPathspages
を使う
CSR(SPA)
- クライアントのJavaScriptでHTMLを生成する。SSRやSSGに比べて読み込みに時間がかかる。SEOも弱い
- APIを叩くときは、useEffectを使ってfetchをするか、SWRやtanstack queryなどを使う(SWRやtanstack queryを使うのが推奨。useEffectの中にfetchをやる方法は古いらしい)

データ取得
概要
データ取得は、SSR or SSG ISRがある。
使用例は以下にある。
SSGの場合
getStaticPropsを使う
- getStaticPropsは静的ジェネレートのタイミングで動く
- propsにgetした値を詰める
import type { InferGetStaticPropsType, GetStaticProps } from 'next'
type Repo = {
name: string
stargazers_count: number
}
export const getStaticProps = (async (context) => {
const res = await fetch('https://api.github.com/repos/vercel/next.js')
const repo = await res.json()
return { props: { repo } }
}) satisfies GetStaticProps<{
repo: Repo
}>
export default function Page({
repo,
}: InferGetStaticPropsType<typeof getStaticProps>) {
return repo.stargazers_count
}
getStaticPropsを使うタイミング
- SSGの時
- ヘッドレスCMSを使う時
- ISRの時も使える
getStaticPropsで、外部APIを叩く場合、APIルートで取得すると、外部APIとAPIルートの二つAPIを叩くことになるので、libフォルダなどに別途外部APIを叩く処理を書く。
例: lib/load-posts.js に micro CMSのAPIを叩く処理を書いて、pages配下のファイルで、lib/load-posts.jsの関数を使う感じ
getStaticPaths
APIで取得した値によってパスを変える時?
[id].tsxみたいな時に使う?
SSGは使う予定がないので、一旦これくらいにしておく
フォームを使用する場合
APIルートで、フォーム用のAPIを作っておく
バリデーションはzodなどを使う。
エラー・ローディング・リダイレクトなどの実装も公式に載ってた。
エラーやローディングはグローバルに使えるものを作りたいので、あまり参考にしなくていいかも。
SSRの場合
getServerSidePropsを使う。
import type { InferGetServerSidePropsType, GetServerSideProps } from 'next'
type Repo = {
name: string
stargazers_count: number
}
export const getServerSideProps = (async () => {
// Fetch data from external API
const res = await fetch('https://api.github.com/repos/vercel/next.js')
const repo: Repo = await res.json()
// Pass data to the page via props
return { props: { repo } }
}) satisfies GetServerSideProps<{ repo: Repo }>
export default function Page({
repo,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
return (
<main>
<p>{repo.stargazers_count}</p>
</main>
)
}
使い所
- 個人情報を扱うとき
- 認証/認可系
メモ
- getServerSidePropsでエラーが出ると、500エラーになる。
- getServerSidePropsはデータの再取得に気を使わないといけなそう(キャッシュの関係で。revalidateとか使うんかなと)
ISRの場合
SSG(getStaticProps)で、決まったタイミングで、データの再取得をする。
例えば、revalidate: 10とすると、10秒おきに、キャッシュを更新(データの再取得をする。)
定期的にデータを更新して、1個前のデータを返すイメージ(私の個人的理解なのであってるかわからん)
とりあえず使う予定がないのでこれくらいで。
CSRの場合
- クライアント側のデータ取得
- コンポーネントレベルでデータ取得できる
CSRの場合の実装方法
useEffectを使った方法
fetchを使っている。でもどうやらこれは古いやり方らしい。
import { useState, useEffect } from 'react'
function Profile() {
const [data, setData] = useState(null)
const [isLoading, setLoading] = useState(true)
useEffect(() => {
fetch('/api/profile-data')
.then((res) => res.json())
.then((data) => {
setData(data)
setLoading(false)
})
}, [])
if (isLoading) return <p>Loading...</p>
if (!data) return <p>No profile data</p>
return (
<div>
<h1>{data.name}</h1>
<p>{data.bio}</p>
</div>
)
}
SWRを使った方法
useSWRを使った方法。
これが公式的におすすめとのこと。
キャッシュやrevalidateなども設定できる。
import useSWR from 'swr'
const fetcher = (...args) => fetch(...args).then((res) => res.json())
function Profile() {
const { data, error } = useSWR('/api/profile-data', fetcher)
if (error) return <div>Failed to load</div>
if (!data) return <div>Loading...</div>
return (
<div>
<h1>{data.name}</h1>
<p>{data.bio}</p>
</div>
)
}
参考になりそうなZenn記事
useSWRの公式