Open3

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

yuu_aoringoyuu_aoringo

はじめに

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

yuu_aoringoyuu_aoringo

レンダリング

概要

  • デフォルトでは、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をやる方法は古いらしい)
yuu_aoringoyuu_aoringo

データ取得

概要

データ取得は、SSR or SSG ISRがある。

使用例は以下にある。

https://nextjs.org/docs/pages/building-your-application/data-fetching

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記事
https://zenn.dev/syu/articles/9a6a5a43e49610

useSWRの公式
https://swr.vercel.app/ja/docs/getting-started