React Router v7 已發布。 查看文件
錯誤處理

錯誤處理

Remix 在 Web 應用程式錯誤處理方面樹立了新的先例,您一定會喜歡的。Remix 會自動捕捉您程式碼中的大多數錯誤,無論是在伺服器端還是在瀏覽器中,並渲染最接近錯誤發生位置的 ErrorBoundary。如果您熟悉 React 的 componentDidCatchgetDerivedStateFromError 類別組件鉤子,它就像那樣,但對於伺服器上的錯誤有一些額外的處理。

Remix 會自動捕捉錯誤,並在以下情況下渲染最近的錯誤邊界:

  • 在瀏覽器中渲染時
  • 在伺服器上渲染時
  • 在初始伺服器渲染文件請求期間的 loader
  • 在初始伺服器渲染文件請求期間的 action
  • 在瀏覽器中的用戶端轉換期間的 loader 中 (Remix 會序列化錯誤並透過網路將其傳送到瀏覽器)
  • 在瀏覽器中的用戶端轉換期間的 action

根錯誤邊界

預設情況下,Remix 附帶內建的預設 ErrorBoundary,但我們希望您能在自己的全域錯誤邊界中新增一些品牌形象。您可以透過從 app/root.tsx 匯出您自己的 ErrorBoundary 來做到這一點。這是在拋出未捕捉到的錯誤時,您的使用者將會看到的內容。

export function ErrorBoundary() {
  const error = useRouteError();
  console.error(error);
  return (
    <html>
      <head>
        <title>Oh no!</title>
        <Meta />
        <Links />
      </head>
      <body>
        {/* add the UI you want your users to see */}
        <Scripts />
      </body>
    </html>
  );
}

您會想要確保仍然渲染 LinksMetaScripts 組件,因為當渲染根錯誤邊界時,整個文件將會掛載和卸載。

巢狀錯誤邊界

階層中的每個路由都是潛在的錯誤邊界。如果巢狀路由匯出錯誤邊界,則將在該處捕捉和渲染其下方的任何錯誤。這表示父路由中其餘的周圍 UI 會繼續正常渲染,因此使用者能夠點擊另一個連結,而不會遺失他們可能擁有的任何用戶端狀態。

例如,考慮以下路由

app/
├── routes/
│   ├── sales.tsx
│   ├── sales.invoices.tsx
│   └── sales.invoices.$invoiceId.tsx
└── root.tsx

如果 app/routes/sales.invoices.$invoiceId.tsx 匯出 ErrorBoundary,並且在其組件、actionloader 中拋出錯誤,則應用程式的其餘部分會正常渲染,且只有頁面的發票部分會渲染錯誤。

error in a nested route where the parent route's navigation renders normally

如果路由沒有錯誤邊界,則錯誤會「向上冒泡」到最近的錯誤邊界,一路到根,因此您不必在每個路由中都新增錯誤邊界,只有在您想要為 UI 新增額外的觸感時才需要。

錯誤清理

在生產模式下,伺服器上發生的任何錯誤都會被自動清理,以防止將任何敏感的伺服器資訊(例如堆疊追蹤)洩漏給客戶端。這表示您從 useRouteError 收到的 Error 實例將會具有通用的訊息,而沒有堆疊追蹤。

export async function loader() {
  if (badConditionIsTrue()) {
    throw new Error("Oh no! Something went wrong!");
  }
}

export function ErrorBoundary() {
  const error = useRouteError();
  // When NODE_ENV=production:
  // error.message = "Unexpected Server Error"
  // error.stack = undefined
}

如果您需要記錄這些錯誤或將其回報給第三方服務,例如 BugSnagSentry,那麼您可以透過在您的 app/entry.server.js 中匯出一個 handleError 來完成。此方法會接收未經清理的錯誤版本,因為它也是在伺服器上執行的。

如果您想觸發錯誤邊界並在瀏覽器中顯示特定的訊息或資料,那麼您可以從 action/loader 拋出一個具有該資料的 Response

export async function loader() {
  if (badConditionIsTrue()) {
    throw new Response("Oh no! Something went wrong!", {
      status: 500,
    });
  }
}

export function ErrorBoundary() {
  const error = useRouteError();
  if (isRouteErrorResponse(error)) {
    // error.status = 500
    // error.data = "Oh no! Something went wrong!"
  }
}
文件和範例依 MIT