跳到主要內容

錯誤處理

錯誤可以分為兩種類別:預期錯誤未捕獲的例外

  • 將預期錯誤建模為回傳值:避免在伺服器動作中對預期錯誤使用 try/catch。使用 useActionState 來管理這些錯誤並將它們回傳給用戶端。
  • 對非預期錯誤使用錯誤邊界:實作使用 error.tsxglobal-error.tsx 檔案的錯誤邊界,以處理非預期錯誤並提供備用 UI。

處理預期錯誤

預期錯誤是在應用程式正常運作期間可能發生的錯誤,例如來自伺服器端表單驗證或失敗請求的錯誤。這些錯誤應明確處理並回傳給用戶端。

處理來自伺服器動作的預期錯誤

使用 useActionState Hook 來管理伺服器動作的狀態,包括處理錯誤。這種方法避免了對預期錯誤使用 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')
}

然後,您可以將您的動作傳遞給 useActionState Hook,並使用回傳的 state 來顯示錯誤訊息。

app/ui/signup.tsx
'use client'
 
import { useActionState } from 'react'
import { createUser } from '@/app/actions'
 
const initialState = {
  message: '',
}
 
export function Signup() {
  const [state, formAction, pending] = useActionState(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 disabled={pending}>Sign up</button>
    </form>
  )
}

您也可以使用回傳的 state 從用戶端元件顯示 Toast 訊息。

處理來自伺服器元件的預期錯誤

當在伺服器元件內部抓取資料時,您可以使用回應來有條件地渲染錯誤訊息或 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 '...'
}

未捕獲的例外

未捕獲的例外是表示錯誤或問題的非預期錯誤,這些錯誤不應在應用程式的正常流程中發生。這些應透過拋出錯誤來處理,然後這些錯誤將被錯誤邊界捕獲。

  • 常見: 使用 error.js 處理根版面配置下方的未捕獲錯誤。
  • 選用: 使用巢狀 error.js 檔案 (例如 app/dashboard/error.js) 處理細微的未捕獲錯誤
  • 不常見: 使用 global-error.js 處理根版面配置中的未捕獲錯誤。

使用錯誤邊界

Next.js 使用錯誤邊界來處理未捕獲的例外。錯誤邊界會捕獲其子元件中的錯誤,並顯示備用 UI,而不是崩潰的元件樹。

透過在路由區段內新增 error.tsx 檔案並匯出 React 元件來建立錯誤邊界

app/dashboard/error.tsx
'use client' // Error boundaries must be Client Components
 
import { useEffect } from 'react'
 
export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  useEffect(() => {
    // Log the error to an error reporting service
    console.error(error)
  }, [error])
 
  return (
    <div>
      <h2>Something went wrong!</h2>
      <button
        onClick={
          // Attempt to recover by trying to re-render the segment
          () => reset()
        }
      >
        Try again
      </button>
    </div>
  )
}

如果您希望錯誤冒泡到父錯誤邊界,您可以在渲染 error 元件時 throw

處理巢狀路由中的錯誤

錯誤將冒泡到最近的父錯誤邊界。這允許透過將 error.tsx 檔案放置在路由階層中的不同層級來進行細微的錯誤處理。

Nested Error Component Hierarchy

處理全域錯誤

雖然較不常見,但即使在使用國際化時,您也可以使用位於根應用程式目錄中的 app/global-error.js 來處理根版面配置中的錯誤。全域錯誤 UI 必須定義自己的 <html><body> 標籤,因為它在啟用時會取代根版面配置或樣板。

app/global-error.tsx
'use client' // Error boundaries must be Client Components
 
export default function GlobalError({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  return (
    // global-error must include html and body tags
    <html>
      <body>
        <h2>Something went wrong!</h2>
        <button onClick={() => reset()}>Try again</button>
      </body>
    </html>
  )
}