跳至內容

13

錯誤處理

在上一章中,您學習了如何使用伺服器動作變更資料。現在讓我們看看如何使用 JavaScript 的 try/catch 陳述式和 Next.js API 優雅地處理錯誤。

本章內容...

以下是我們將涵蓋的主題

如何使用特殊的 error.tsx 檔案來捕捉路由區段中的錯誤,並向使用者顯示備用 UI。

如何使用 notFound 函式和 not-found 檔案來處理 404 錯誤(針對不存在的資源)。

try/catch 新增至伺服器動作

首先,讓我們將 JavaScript 的 try/catch 陳述式新增到您的伺服器動作中,以便您能優雅地處理錯誤。

如果您知道如何執行此操作,請花幾分鐘時間更新您的伺服器動作,或者您可以複製以下程式碼

請注意 redirect 是在 try/catch 區塊**之外**被呼叫的。這是因為 redirect 的運作方式是拋出一個錯誤,而這個錯誤會被 catch 區塊捕捉到。為了避免這種情況,您可以在 try/catch **之後**呼叫 redirect。如此一來,只有在 try 成功的情況下,才會執行到 redirect

現在,讓我們來看看在伺服器動作中拋出錯誤時會發生什麼事。您可以透過提前拋出錯誤來做到這一點。例如,在 deleteInvoice 動作中,在函式頂部拋出一個錯誤。

/app/lib/actions.ts
export async function deleteInvoice(id: string) {
  throw new Error('Failed to Delete Invoice');
 
  // Unreachable code block
  try {
    await sql`DELETE FROM invoices WHERE id = ${id}`;
    revalidatePath('/dashboard/invoices');
    return { message: 'Deleted Invoice' };
  } catch (error) {
    return { message: 'Database Error: Failed to Delete Invoice' };
  }
}

當您嘗試刪除發票時,您應該會在 localhost 上看到一個錯誤。請確保在測試後,以及繼續進行下一節之前,移除這個錯誤。

在開發過程中,看到這些錯誤訊息很有幫助,因為您可以及早發現任何潛在的問題。然而,您也希望向使用者顯示錯誤訊息,以避免程式突然中止,並允許您的應用程式繼續運行。

這就是 Next.js 的 error.tsx 檔案的用途。

使用 error.tsx 處理所有錯誤

error.tsx 檔案可以用來定義路由區段的 UI 邊界。它作為所有未預期錯誤的**捕捉器**,並允許您向使用者顯示一個備援 UI。

在您的 /dashboard/invoices 資料夾內,建立一個名為 error.tsx 的新檔案,並貼上以下程式碼:

/dashboard/invoices/error.tsx
'use client';
 
import { useEffect } from 'react';
 
export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  useEffect(() => {
    // Optionally log the error to an error reporting service
    console.error(error);
  }, [error]);
 
  return (
    <main className="flex h-full flex-col items-center justify-center">
      <h2 className="text-center">Something went wrong!</h2>
      <button
        className="mt-4 rounded-md bg-blue-500 px-4 py-2 text-sm text-white transition-colors hover:bg-blue-400"
        onClick={
          // Attempt to recover by trying to re-render the invoices route
          () => reset()
        }
      >
        Try again
      </button>
    </main>
  );
}

關於上面的程式碼,您會注意到以下幾點:

  • "use client" - error.tsx 必須是一個客戶端元件。
  • 它接受兩個屬性:
    • error:這個物件是 JavaScript 原生 Error 物件的實例。
    • reset:這是一個用於重置錯誤邊界的函式。執行時,該函式會嘗試重新渲染路由區段。

當您再次嘗試刪除發票時,您應該會看到以下 UI:

The error.tsx file showing the props it accepts

使用 notFound 函式處理 404 錯誤

另一種您可以優雅地處理錯誤的方式是使用 notFound 函式。雖然 error.tsx 對於捕捉**所有**錯誤很有用,但 notFound 可以在您嘗試擷取不存在的資源時使用。

例如,請造訪 https://127.0.0.1:3000/dashboard/invoices/2e94d1ed-d220-449f-9f11-f0bbceed9645/edit

這是一個在您的資料庫中不存在的虛擬 UUID。

您會立即看到 error.tsx 開始運作,因為這是 /invoices 的子路由,而 error.tsx 正是在那裡定義的。

然而,如果您想要更精確一點,您可以顯示 404 錯誤來告訴使用者他們嘗試訪問的資源並不存在。

您可以透過進入 data.ts 中的 fetchInvoiceById 函式,並在控制台中記錄返回的 invoice 來確認資源不存在。

/app/lib/data.ts
export async function fetchInvoiceById(id: string) {
  noStore();
  try {
    // ...
 
    console.log(invoice); // Invoice is an empty array []
    return invoice[0];
  } catch (error) {
    console.error('Database Error:', error);
    throw new Error('Failed to fetch invoice.');
  }
}

現在您知道發票在資料庫中不存在,讓我們使用 notFound 來處理它。導覽至 /dashboard/invoices/[id]/edit/page.tsx,並從 'next/navigation' 匯入 { notFound }

然後,您可以使用條件式來呼叫 notFound,如果發票不存在的話。

/dashboard/invoices/[id]/edit/page.tsx
import { fetchInvoiceById, fetchCustomers } from '@/app/lib/data';
import { updateInvoice } from '@/app/lib/actions';
import { notFound } from 'next/navigation';
 
export default async function Page(props: { params: Promise<{ id: string }> }) {
  const params = await props.params;
  const id = params.id;
  const [invoice, customers] = await Promise.all([
    fetchInvoiceById(id),
    fetchCustomers(),
  ]);
 
  if (!invoice) {
    notFound();
  }
 
  // ...
}

完美!如果找不到特定發票,<Page> 現在會拋出錯誤。要向使用者顯示錯誤 UI,請在 /edit 資料夾內建立一個 not-found.tsx 檔案。

The not-found.tsx file inside the edit folder

然後,在 not-found.tsx 檔案中,貼上以下程式碼:

/dashboard/invoices/[id]/edit/not-found.tsx
import Link from 'next/link';
import { FaceFrownIcon } from '@heroicons/react/24/outline';
 
export default function NotFound() {
  return (
    <main className="flex h-full flex-col items-center justify-center gap-2">
      <FaceFrownIcon className="w-10 text-gray-400" />
      <h2 className="text-xl font-semibold">404 Not Found</h2>
      <p>Could not find the requested invoice.</p>
      <Link
        href="/dashboard/invoices"
        className="mt-4 rounded-md bg-blue-500 px-4 py-2 text-sm text-white transition-colors hover:bg-blue-400"
      >
        Go Back
      </Link>
    </main>
  );
}

重新整理路由,您現在應該會看到以下 UI

404 Not Found Page

這一點需要注意,notFound 的優先順序會高於 error.tsx,因此當您想要處理更特定的錯誤時,您可以使用它!

延伸閱讀

要了解更多關於 Next.js 中的錯誤處理,請查看以下文件:

您已完成本章13

很好,您現在可以在您的應用程式中妥善處理錯誤了。

下一步

14:提升無障礙使用體驗

讓我們繼續探索提升使用者體驗的方法。您將學習伺服器端表單驗證和提升無障礙使用體驗。