メインコンテンツまでスキップ

エラー処理

エラーは次の2つのカテゴリーに分けられます:予期されたエラー未捕捉の例外

  • 予期されたエラーを戻り値としてモデル化する:Server Actionsで予期されたエラーにはtry/catchを避けましょう。useFormStateを使ってこれらのエラーを管理し、クライアントに返します
  • 予期しないエラーにはエラーバウンダリを使用するerror.tsxglobal-error.tsxファイルを使用してエラーバウンダリを実装し、予期しないエラーを処理し、フォールバックUIを提供します

予期されたエラーの処理

予期されたエラーとは、アプリケーションの通常の動作中に発生する可能性のあるエラーのことです。例えば、サーバーサイドのフォームバリデーションや失敗したリクエストが含まれます。これらのエラーは明示的に処理し、クライアントに返す必要があります。

Server Actionsから予期されたエラーを処理する

useFormStateフックを使用してServer Actionsの状態を管理し、エラーを処理します。この方法は、予期されたエラーを例外として投げずに戻り値としてモデル化することであり、try/catchブロックを回避します。

app/actions.ts
'use server'

import { redirect } from 'next/navigation'

// ユーザーを作成する関数
export async function createUser(prevState: any, formData: FormData) {
const res = await fetch('https://...')
const json = await res.json()

if (!res.ok) {
// メールアドレスが有効か確認してください
return { message: 'Please enter a valid email' }
}

redirect('/dashboard')
}

その後、アクションをuseFormStateフックに渡し、返されたstateを使用してエラーメッセージを表示することができます。

app/ui/signup.tsx
'use client'

import { useFormState } from 'react'
import { createUser } from '@/app/actions'

const initialState = {
message: '',
}

export function Signup() {
const [state, formAction] = useFormState(createUser, initialState)

return (
<form action={formAction}>
<label htmlFor="email">Email</label>
<input type="text" id="email" name="email" required />
{/* ... */}
<p aria-live="polite">{state?.message}</p>
<button>Sign up</button>
</form>
)
}

Good to know: これらの例は、Next.jsのApp Routerに付属のReactのuseFormStateフックを使用しています。React 19を使用している場合は、代わりにuseActionStateを利用してください。詳細はReactのドキュメントを参照してください。

クライアントコンポーネントからトーストメッセージを表示するために返された状態を使用することもできます。

サーバーコンポーネントから予期されたエラーを処理する

Server Componentの内部でデータをフェッチする場合、レスポンスを使用してエラーメッセージを条件付きでレンダリングするか、redirectを行います。

app/page.tsx
export default async function Page() {
const res = await fetch(`https://...`)
const data = await res.json()

if (!res.ok) {
return 'There was an error.'
}

return '...'
}

未捕捉の例外

未捕捉の例外は、アプリケーションの通常のフロー中に発生してはならないバグや問題を示す予期しないエラーです。これらはエラーとして投げて、エラーバウンダリによって捕捉されるべきです。

  • 共通のケース:root レイアウト下で未捕捉エラーを処理するにはerror.jsを使用
  • オプションのケース:ネストされたerror.jsファイル(例:app/dashboard/error.js)を使用して詳細な未捕捉エラーを処理
  • まれなケース:root レイアウトで未捕捉エラーを処理するにはglobal-error.jsを使用

Error boundary の使用

Next.jsは error boundary を使用して未捕捉の例外を処理します。Error boundary はその子コンポーネントで発生したエラーを捕捉し、クラッシュしたコンポーネントツリーの代わりにフォールバックUIを表示します。

ルートセグメント内にerror.tsxファイルを追加し、Reactコンポーネントをエクスポートすることで error boundary を作成します。

app/dashboard/error.tsx
'use client' // エラーバウンダリはクライアントコンポーネントである必要があります

import { useEffect } from 'react'

export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
useEffect(() => {
// エラーをエラーレポートサービスにログとして記録
console.error(error)
}, [error])

return (
<div>
<h2>Something went wrong!</h2>
<button
onClick={
// セグメントを再レンダリングしようと試みて回復を試みる
() => reset()
}
>
Try again
</button>
</div>
)
}

エラーを親の error boundary にバブルアップさせたい場合は、errorコンポーネントをレンダリングする際にthrowすることができます。

ネストされたルートでのエラー処理

エラーは最も近い親の error boundary にバブルアップします。これにより、ルート階層の異なるレベルにerror.tsxファイルを配置することで、詳細なエラー処理が可能になります。

ネストされたエラーコンポーネント階層ネストされたエラーコンポーネント階層

グローバルエラーの処理

まれですが、国際化を活用する場合でも、root レイアウトを使用してエラーを処理することができます。グローバルエラーUIは独自の<html>タグと<body>タグを定義する必要があります。これは有効時にroot レイアウトやテンプレートを置き換えるためです。

app/global-error.tsx
'use client' // エラーバウンダリはクライアントコンポーネントである必要があります

export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
// global-errorはhtmlとbodyタグを含める必要があります
<html>
<body>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</body>
</html>
)
}