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