Next.js のチュートリアルのメモ

Next.js のチュートリアルを学習しながらの雑なメモ。
現時点ではバージョンは15。
chapter 1 で学んだこと|pnmp とディレクトリ構成
パッケージマネージャーは、pnpm を推奨してる。
ディレクトリ構成
- /app : ルートやコンポーネントを編集する一番作業するディレクトリ。
- /app/lib : アプリで使用する再利用関数、データ取得関数、プレースホルダーデータ(ダミーのDBデータ)などを管理。
- /app/ui : アプリのUIコンポーネントを管理。
- /public : 画像のような静的なアセット。
- next.config.ts : プロジェクトのコンフィグ。
chapter 2 で学んだこと|Next.js の css
CSS は Tailwind と CSS modules を基本で使える。特に Tailwind を推奨しているように見えるけど、現時点では個人的に少し気持ち悪く感じる。他にも CSS-in-JS や普通の sass も使える。
CSS modules がいいかも?
clsx というライブラリーを使うと、状態の応じて className のCSSクラスを付け替えることができる。
chapter 3 で学んだこと|画像とフォントの最適化
- next/font コンポーネントを使い、ビルド時にフォントを静的アセットにして最適化する。
- next/image コンポーネントを使い、ビルド時に画像を最適化する。
chapter 4 で学んだこと|layout.tsx と page.tsx
ページ構成について
ページは /app ディレクトリ以下に page.tsx を作る。 /app ディレクトリがルートになる。
サブページはそのページのディレクトリを作成しその中に page.tsx ファイル作る。
例)http://localhost:3000/customers/ であれば /app/customers/page.tsx の構成になる。
レイアウトについて
サブディレクトリの page.tsx と同じ階層に layout.tsx を作成すると、自動的に page.tsx が props として渡り、layout.tsx の {children} の部分に表示される。
さらに下層のディレクトリにある page.tsx の場合、同階層に layout.tsx 無いと上階層の layout.tsx のレイアウトが使われる。
/app 直下の layout.tsx はルートレイアウトであり、Next.js の全てのページで共通で使用される。
ページ遷移時には、page.tsx のみがレンダリングされ layout.tsx で共通化されている部分はレンダリングされない。
chapter 5 で学んだこと|Link コンポーネント
<Link> コンポーネントについて
Next.js では <a> タグの代わりに <Link> コンポーネントを使う。
<Link> コンポーネントを使うと <Link> コンポーネントが表示された時に、リンク先のコードが事前に読み込まれるのでページ遷移が高速になる。
自動コード分割とプリフェッチングという。
現在のページのパスを取得する
usePathname() を使うことで、現在ユーザーが見ているページのパスを取得することができる。
usePathname() は React のフックなので、クライアントコンポーネントに変換する必要がある。
使用するファイル内の最上部へ ‘use client’ 追加し、next/navigation から usePathname() をインポートする。
chapter 6 で学んだこと|GitHub と Vercel と Neon
GitHub と Vercel を連携させる
GitHub 上のリモートリポジトリにアップし、その GitHub のリポジトリを Vercel と連携設定しデプロイする。
Neon(Postgre Database)を使えるようにする
Vercel のデータベース機能の一つ Neon を作成して、Next.js のプロジェクトから接続する。
.env ファイルは、git にトラッキングされないようにするために .gitignore に記述されているかを確認する。
chapter 7 で学んだこと|データベースからデータ取得(フェッチ)
データベースからのデートの取得について
Next.js でデータを取得するときのデフォルトは React Server Components。
他に APIレイヤー を作成し、fetch でエンドポイントからデータを取得することができる。API は複数のページや外部アプリで再利用したい時に便利。
クエリを実行するには postgres.js ライブラリーと SQL を使う。
リクエストウォーターフォール
条件違いの複数のデータを SQL で同時にリクエストする場合に await を使って順番に取得していくこと。
先に取得したデータが次のリクエストに必要などのケースもあるが、基本的には順番待ちになるのでパフォーマンスが悪くなる。
パラレルデータフェッチング(並行データ取得)
promise.all を使うことで同時に条件違いのリクエストをし、全て取得するまで待つことができる。
ただし、1つだけ極端に遅いリクエストがあった場合に全体に影響がある。
chapter 8 で学んだこと|Static Rendering と Dynamic Rendering
Next.js のレンダリングにはデプロイ時に静的ページを生成するスタティックレンダリングと、取得している情報が変更されるとリアルタイムで表示も更新されるダイナミックレンダリングがある。
Static Rendering
リアルタイムで更新しないプログやプロダクトページに向いている。
Dynamic Rendering
ダイナミックレンダリングには、以下のような利点がある。
- リアルタイムで頻繁にデータが変更されるアプリケーション。
- ページユーザーの操作に基づいてデータを更新するような、ダッシュボードやユーザー固有ページ。
ダイナミックレンダリングを使用すると、リクエスト時に Cookie や URL 検索パラメーターなどの情報にアクセスできる。
chapter 9 で学んだこと|loading.tsx と Suspense を使ったストリーミング
ストリーミング
データを小さく分割しサーバーからクライアントに段階的に転送すること。
フェッチでデータ取得に時間のかかる時に使うといい。
loading.tsx または Suspense を使用した方法がある。
ローディング中はフォールバックでスケルトン(簡易的なコンポーネント)表示をする。
loading.tsx
loading.tsx は特別なファイル名でージレベルでのストリーミング。
作成するとその階層以下のページに適応される。
サイドバーなどは普通のコンポーネントで、コンテンツ部分全体に適応する場合には使う。自動的に Suspense が作成される。
Suspense
コンポーネントレベルの細かいコントロールができる。
ページ全体の表示がデータフェッチングしている遅いコンポーネントに影響される場合などに、その部分に Suspense を使うことにより、ページ表示のブロッキングを防ぐことができる。
ルートグループ
Next.js の URL は基本的にディレクトリ構成になるが、ディレクトリ名に () をつけた場合には、そのディレクトリは URL の階層としては無視される。
loading.tsx などその階層以下に適応させたく無い場合にはルートグループを使って上手く分けす。
chapter 10 で学んだこと|Partial Prerendering (PPR)
パーシャルレンダリングとは、静的レンダリング、動的レンダリング、ストリーミングを組み合わせてレンダリングする方法。
Next.js 15 ではまだ実験的な機能で、本番環境では推奨されていない。が、将来的には Next.js のデフォルトのレンダリング方法になるかもしれない。
PPR は next.config.ts と layout.ts に設定を記述を追加すれば、基本的にコードを変更する必要がなく、ルートの動的な部分を Suspense でラップしている限り、静的な部分と動的な部分を自動で認識して動作してくれる。
chapter 11 で学んだこと|useSearchParams, usePathname, useRouter フック
検索で使うフックの機能
- useSearchParams:現在の URL のパラメーターにアクセスできる。
- usePathname:現在の URL のパス名を読み取ることができる。
- useRouter:現在のルートに関連する機能や情報を取得・操作する。ページを移動などできる。
URL を使い検索機能を実装
上記の フック(API) を使って検索用の input に入力したテキストを onChange でリアルタイムに取得し、useRouter を使って URL に反映させ、その URL の検索クエリにマッチした情報を表示するようにした。
検索入力をする search.tsx は、イベントリスナー(onChange) とフックを使うのでクライアントコンポーネントにする ‘use client’ をファイルの先頭に付ける。
URL 検索パラメータを使用する利点
- ブックマークや共有可能なURL:ーザーは検索クエリやフィルターを含むアプリケーションの現在の状態をブックマークし、後で参照したり共有したりすることができる。
- サーバーサイドレンダリング:URL パラメータをサーバー上で直接使用して初期状態のレンダリングできるため、サーバー レンダリングの処理が容易になります。
- 分析と追跡:検索クエリとフィルターを URL に直接含めることで、追加のクライアント側ロジックを必要とせずにユーザーの行動を追跡しやすくなる。
入力時の onChange で発火を制御する
input に onChange だとキーを打つごとに発火するのでデバウンス処理をして、キーを打つがストップして 500ms 経過すると発火するように制御する。 useDebouncedCallback を使うと簡単に設定できる。
chapter 12 で学んだこと|React Server Actions
React Server Actions
サーバーアクションは form 要素の action 属性に関数を設定して、直接サーバー関数を呼び出す。(API ルートを作ってフェッチを使わずにサーバー側で処理を実行できる。)
‘use server’
サーバーアクションを記述する actions.ts は先頭に ‘use server’ ディレクティブを付ける。以下のようなサーバー専用の処理を簡単にできるようになる。
- データベース操作
- 外部 API のコール(サーバー側でのみ処理し、クライアントに API キーを公開しない)
- フォームの処理(クライアント側から安全にデータを受け取る)
FormData
関数側はキャプチャされた FormData オブジェクトを自動的に受け取り、アクション内でデータの検証などを行い sql でデータベースへ保存や変更、削除ができる。
データの検証と変更
入力されたデータは formData オブジェクトから formData.get() で抽出して、データベースへ保存に適した形式か検証し必要であれば変更をする。
検証は手動でも可能だがチュートリアルでは Zod という TypeScript First で検証できるライブラリーを使用する。
SQL でデータベースに保存
‘postgres’ をインポートして、検証したデータを sql 文で保存。
revalidate と redirect
保存した後 revalidatePath(‘path’) を行いそのパスに関連したキャッシュを削除しリダイレクトする。
recalidatePath() をしないと追加したデータ保存前の状態がキャッシュされているので、保存したデータがすぐに表示されない。
サーバーアクションに id を渡す場合
既にデータベースに保存されているデータを変更や削除する場合に、サーバーアクションに id を渡したい場合があるが、form の action で呼んでいる関数の引数として渡すことはできない。代わりに Javascript の bind を使う。
// NG
return (
<form action={updateInvoice(id)}>
...
)
// OK
const updateInvoiceWithId = updateInvoice.bind(null, invoice.id);
return (
<form action={updateInvoiceWithId}>
...
)
chapter 13 で学んだこと|エラーの処理
エラーの処理には error.tsx と notFound() 関数を使う2つがある。
error.tsx
ルートセグメント内のエラーをキャッチして、フォールバックUIを表示できる。
エラーを適切に処理できるように、エラーを検出したいで try/catch を使う。(データベースからデータ取得時など)