跳到內容

如何處理錯誤

錯誤可以分為兩類:預期錯誤未捕獲的例外。本頁面將引導您瞭解如何在您的 Next.js 應用程式中處理這些錯誤。

處理預期錯誤

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

伺服器動作

您可以使用useActionState Hook 來管理伺服器函式的狀態並處理預期錯誤。避免對預期錯誤使用 try/catch 區塊。相反地,您可以將預期錯誤建模為傳回值,而不是作為拋出的例外。

app/actions.ts
'use server'
 
export async function createPost(prevState: any, formData: FormData) {
  const title = formData.get('title')
  const content = formData.get('content')
 
  const res = await fetch('https://api.vercel.app/posts', {
    method: 'POST',
    body: { title, content },
  })
  const json = await res.json()
 
  if (!res.ok) {
    return { message: 'Failed to create post' }
  }
}

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

app/ui/form.tsx
'use client'
 
import { useActionState } from 'react'
import { createPost } from '@/app/actions'
 
const initialState = {
  message: '',
}
 
export function Form() {
  const [state, formAction, pending] = useActionState(createPost, initialState)
 
  return (
    <form action={formAction}>
      <label htmlFor="title">Title</label>
      <input type="text" id="title" name="title" required />
      <label htmlFor="content">Content</label>
      <textarea id="content" name="content" required />
      {state?.message && <p aria-live="polite">{state.message}</p>}
      <button disabled={pending}>Create Post</button>
    </form>
  )
}

伺服器組件

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

找不到

您可以在路由區段內呼叫notFound函式,並使用not-found.js檔案來顯示 404 UI。

app/blog/[slug]/page.tsx
import { getPostBySlug } from '@/lib/posts'
 
export default async function Page({ params }: { params: { slug: string } }) {
  const post = getPostBySlug((await params).slug)
 
  if (!post) {
    notFound()
  }
 
  return <div>{post.title}</div>
}
app/blog/[slug]/not-found.tsx
export default function NotFound() {
  return <div>404 - Page Not Found</div>
}

處理未捕獲的例外

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

巢狀錯誤邊界

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

透過在路由區段內新增一個error.js檔案並匯出 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.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>
  )
}