📝

非公式Next.js14の日本語ドキュメント読んだ時のメモ

2024/05/12に公開

非公式Next.js14の日本語ドキュメント読んだ時のメモ

https://ja.next-community-docs.dev/docs/app-router

業務で必要になったのでNext.js14のドキュメントを読んだ。Next.jsは初めて使う。react.jsは経験あり。Next.jsドキュメントの「アプリケーションの構築」を全て読んだのでその時のメモ。

ルーティングについて

ページとルーティング

レイアウトについて

  • layout.tsは共有されるUIコンポーネントで状態を保持する
  • ルートレイアウトは必須html,bodyタグを含む必要がある
  • ルートレイアウトは全てのルートに適用される
  • ルートグループを使うと複数のルートレイアウトを作成できる。
  • ディレクトリ内で定義されたレイアウトはそのルートで適用される。
  • ルートグループを使うと複数のルートにまたがったレイアウトを適用できる。

テンプレートについて

テンプレートは各子レイアウトやページをラップするという点で、レイアウトに似ています。ルートにまたがって永続的に状態を維持するレイアウトとは異なり、テンプレートはナビゲーションの際に子要素ごとに新しいインスタンスを生成します。つまり、ユーザーがテンプレートを共有するルート間をナビゲートすると、コンポーネントの新しいインスタンスがマウントされ、DOM 要素が再作成され、ステートは保存されず、エフェクトは再同期されます。
そのような特定の動作が必要な場合があり、テンプレートはレイアウトよりも適切なオプションです。

👆こういうことやりたいときはtemplate.tsを作成してlayout.tsの代わりに使う。

リンクとナビゲート

スクロールについて

Next.js App Router のデフォルトの動作は、新しいルートの先頭までスクロールするか、前後方向のナビゲーションのためにスクロール位置を維持します。

👆とのこと。無効にしたい場合は下記のようにscroll=falseを記述する。

// next/link
<Link href="/dashboard" scroll={false}>
  Dashboard
</Link>

programableにルートを変更したい場合の記述について

clientコンポーネントなら

'use client'

import { useRouter } from 'next/navigation'

export default function Page() {
  const router = useRouter()

  return (
    <button type="button" onClick={() => router.push('/dashboard')}>
      Dashboard
    </button>
  )
}

みたいにuseRouter()を使う。serverコンポーネントならredirect()を使う。

ナビゲーションについて

公式ドキュメントだけでわからない部分があった。Next.js(ver13)の下記記事が参考になった。
https://qiita.com/marl0401/items/0acb38f40814c18cf54d

インポートルール
  • 🆗サーバーコンポーネントからクライアントコンポーネントはインポートできる。
  • ❌クライアントコンポーネントからサーバーコンポーネントはインポートできない。

プリフェッチについて

プリフェッチはルートが訪問される前にバックグラウンドでプリロードする方法です。
Next.js でルートがプリフェッチされる方法は 2 つあります:
<Link>コンポーネント: ルートは、ユーザのビューポートに表示されるようになると、自動的にプリフェッチされます。プリフェッチは、ページが最初にロードされたときや、スクロールによって表示されたときに行われます。
router.prefetch(): useRouter フックはプログラムでルートをプリフェッチするために使うことができます。

プリフェッチは開発では有効ではなく、本番でのみ有効です。

部分レンダリング

部分レンダリングとは、ナビゲーションで変更されたルートセグメントのみがクライアントで再レンダリングされ、共有セグメントは保存されることを意味します。
たとえば、/dashboard/settings と /dashboard/analytics という 2 つのルート間をナビゲートする場合、settings と analytics ページがレンダリングされ、共有された dashboard のレイアウトは保持されます。

👆試してないけど多分これ使うとページネーションとかをページ遷移させずに動かせる?

ソフトナビゲーション

デフォルトでは、ブラウザはページ間でハードナビゲーションを実行します。これは、ブラウザがページをリロードし、アプリの useState フックなどの React の状態と、ユーザーのスクロール位置やフォーカスされた要素などのブラウザの状態をリセットすることを意味します。しかし Next.js では、アプリルーターはソフトナビゲーションを使用します。つまり、React とブラウザの状態を保持したまま、React は変更されたセグメントだけをレンダリングし、ページ全体をリロードすることはありません。

エラーハンドリング

セグメントごとにerror.tsx作る。これはクライアントコンポーネントでつくる。

ルートの app/error.js 境界は、ルートの app/layout.js または app/template.js コンポーネントで発生したエラーをキャッチしません。
これらのルートコンポーネントのエラーを処理するには、ルートディレクトリ app にある app/global-error.js という error.js のバリエーションを使用してください。
ルートの error.js とは異なり、global-error.js のエラー境界はアプリケーション全体を包み込みます。このため、global-error.js は独自の <html> タグと <body> タグを定義しなければならないことに注意する必要があります。
global-error.jsはもっとも粒度の小さいエラー UI であり、アプリケーション全体のエラー処理を行う "catch-all "と考えることができます。ルートコンポーネントは一般的にあまり動的ではなく、他の error.js 境界がほとんどのエラーをキャッチするため、頻繁にトリガーされることはないでしょう。
global-error.jsが定義されている場合でも、フォールバックコンポーネントがルートレイアウト内でレンダリングされるルートerror.jsを定義することを推奨します。

なのでルートコンポーネントのエラーハンドリング用にglobal-error.jsを作る。

👆こういう仕組みらしいので、この画像でいうとPage内のエラーはハンドリングされるがSideNav,Headerのエラーはハンドリングされず上位のerror.tsxにて処理される。

データフェッチについて

データのフェッチ、キャッシュ、再検証

データのフェッチ方法

  • fetchを使用したサーバー上でのデータフェッチ
  • サードパーティライブラリを使用したサーバー上でのデータフェッチ
  • ルートハンドラを介したクライアント上でのデータフェッチ
  • サードパーティライブラリを使用したクライアント上でのデータフェッチ

fetchを利用したサーバ上でのデータフェッチ

Next.js はネイティブのfetch Web APIを拡張し、サーバー上の各 fetch リクエストに対するキャッシュと再検証の動作を設定できるようになりました。React はfetchを拡張して、React コンポーネントツリーのレンダリング中にフェッチリクエストを自動的にメモします。
async/awaitを使用した Server Components、Route ハンドラおよびServer Actionsでfetchを使用できます。

データのキャッシュ

デフォルトではfetchで返された値をサーバー上のデータ・キャッシュに自動的にキャッシュする。

デフォルトでは、Next.js はfetchで返された値をサーバー上のデータ・キャッシュに自動的にキャッシュします。つまり、データはビルド時またはリクエスト時にfetchされ、キャッシュされて各データリクエストで再利用されます。

// 'force-cache'がデフォルトなので省略可能
fetch('https://...', { cache: 'force-cache' })

データの再検証

再建所を行うことによってデータキャッシュを削除し最新データを取得できる。

再検証とは、データキャッシュを削除し、最新のデータを再取得するプロセスです。これは、データが変更され、最新の情報を確実に表示したい場合に使用します。
キャッシュされたデータは、2 つの方法で再検証できます。

時間ベースの再検証

これは、変更頻度が低く、鮮度がそれほど重要でないデータに有効。

fetch('https://...', { next: { revalidate: 3600 } })

ルート Segment 内のすべてのfetchリクエストを再検証するには、セグメント設定オプションを使用する。

export const revalidate = 3600 // 1時間ごとに再検証
オンデマンドでの再検証

revalidateTagを使う場合。

アクション側

'use server'

import { revalidateTag } from 'next/cache'

export default async function action() {
  revalidateTag('collection')
}

取得処理側

export default async function Page() {
  const res = await fetch('https://...', { next: { tags: ['collection'] } })
  const data = await res.json()
  // ...
}

revalidateTagが実行されると取得処理側で再検証(最新データ取得)が行われる。

キャッシュされないケース

  • cache: 'no-store がfetchリクエストに指定されている
  • revalidate: 0 オプションが個々のfetchリクエストに指定されている
  • fetchリクエストがルートハンドラのPOSTメソッドで使用されている
  • fetchリクエストがheaderまたはcookiesの後で使用されている
  • ルート Segment でconst dynamic = 'force-dynamic'が指定されている
  • ルート Segment のfetchCacheオプションがデフォルトでキャッシュをしないように指定されている
  • fetchリクエストでAuthorizationまたはCookieヘッダーが指定されていて、コンポーネントツリー上にキャッシュされていないリクエストがある

個々のfetchリクエストについて

fetch('https://...', { cache: 'no-store' })

Server Actionsとミューテーション

Server Actionsはサーバー上で実行される非同期関数です。これは、Next.jsアプリケーションでフォームの送信とデータの変更を処理するために、Server Components及びClient Componentsで使用することができます。

  • サーバーコンポーネント・クライアントコンポーネントの両方で使える。
  • サーバーコンポーネントで使う場合はその関数の先頭に'use sever'ディレクティブをつける。
  • クライアントコンポーネントではモジュールレベルで'use server'ディレクティブを使用しているアクションのみインポートできる。
  • ファイルの先頭に'use server'ディレクティブをつけてファイル内の関数をすべてアクションとしてエクスポートすることもできる。

Server Actionsは<form>要素のaction属性を使用して呼び出すことができます。

Server Componentsはデフォルトでプログレッシブエンハンスメントをサポートしており、JavaScriptがまだロードされていないか無効になっていてもフォームは送信されます。
Client Componentsでは、JavaScriptがまだロードされていない場合、Server Actionsを呼び出すフォームは送信がキューイングされ、クライアントのハイドレーションが優先されます。
ハイドレーション後、ブラウザはフォームの送信時にリフレッシュされません。
Server Actionsは<form>に限定されず、イベントハンドラ、useEffect、サードパーティのライブラリ、および<button>などの他のフォーム要素から呼び出すことができます。

Server Actionsは、Next.jsのキャッシュおよび再検証アーキテクチャと統合されています。アクションが呼び出されると、Next.jsは単一のサーバーラウンドトリップで更新されたUIと新しいデータの両方を返すことができます。

舞台裏では、アクションはPOSTメソッドを使用しており、POSTメソッドのみがServer Actionsを呼び出すことができます。

Server Actionsの引数および戻り値は、Reactによってシリアライズ可能である必要があります。シリアライズ可能な引数と返り値 のリストについては、Reactのドキュメントを参照してください。

Server Actionsは関数です。これは、それらをアプリケーション内のどこでも再利用できることを意味します。

Server Actionsは、使用されているページまたはレイアウトからランタイムを継承します。

Server Actionsは、使用されているページまたはレイアウトからのRoute Segment Configを継承します。これにはmaxDurationなどのフィールドも含まれます。

リダイレクト

redirect()を実行する場合はtry/catchブロックの外で呼び出す必要がある。

データのフェッチパターン

推奨パターンとベストプラクティスが書いてる。

可能な限りサーバー上でデータを取得することをお勧めします。これにより、以下のことが可能になります:

バックエンドのデータ・リソース(データベースなど)に直接アクセスできる

アクセストークンや API キーなどの機密情報がクライアントに公開されるのを防ぎ、アプリケーションをより安全に保つ

同じ環境でデータを取得し、レンダリングします。これにより、クライアントとサーバー間の往復通信と、クライアント上のメインスレッドでの作業の両方が削減されます

クライアントへの複数の個別リクエストの代わりに、1 回のラウンドトリップで複数のデータを取得します

クライアントとサーバーのウォーターフォールを減らす

リージョンによっては、データ・フェッチはデータ・ソースの近くで行われ、待ち時間を短縮し、パフォーマンスを向上させることもできます

Server Components、ルートハンドラ、およびサーバーアクションを使用して、サーバー上でデータをフェッチできます

ベストプラクティスにこんなこと書いてた

必要な場所でデータをフェッチする
ツリー内の複数のコンポーネントで同じデータ(現在のユーザーなど)を使用する必要がある場合、グローバルにデータをフェッチしたり、コンポーネント間で prop を転送したりする必要はありません。代わりに、同じデータに対して複数のリクエストを行うことによるパフォーマンスへの影響を心配することなく、データを必要とするコンポーネントでfetchまたは React cacheを使用できます。
これが可能なのは、fetch リクエストが自動的にメモ化されるからです。リクエストのメモ化の詳細についてはこちらを参照してください。

👆この辺は考え方の切り替えが必要そう。

レンダリング

Next.jsでのレンダリング処理がどう進むかについてはServer Components,Client Componentsに詳しく記載があるのでそちらを参照。

構成パターン

サーバー専用のコードをクライアント環境に持ち込まない

JavaScript モジュールはサーバーとクライアントの両方のコンポーネントモジュールで共有できるため、サーバーだけで実行される予定のコードがクライアントにこっそり入り込む可能性があります。

こいうことがあり得るので、防止策としてクライアントコンポーネントで呼ばれたくないアクションは定義するファイルでserver-onlyパッケージをインポートする。

import 'server-only'

export async function getData() {
  const res = await fetch('https://external-service.com/data', {
    headers: {
      authorization: process.env.API_KEY,
    },
  })

  return res.json()
}

これでこのモジュールはサーバー上でしか実行できなくなる。

Discussion