React Router 的 useRouteError:在错误边界中捕获 loader、action 与渲染错误
【免费下载链接】react-routerDeclarative routing for React项目地址: https://gitcode.com/GitHub_Trending/re/react-router
useRouteError是 React Router 提供给路由模块ErrorBoundary使用的 Hook,用于访问某条路由在其loader、action执行或组件渲染过程中抛出的错误对象。本文围绕当前仓库中的 docs/api/hooks/useRouteError.md 展开,结合框架(framework)模式与数据(data)模式的实际用法,并深入到 packages/react-router/lib/hooks.tsx 的实现源码,帮助你掌握useRouteError的调用姿势、返回值的类型收敛技巧,以及错误在 React Router 内部被采集与分发的完整链路。
功能定位:谁能用、何时用
useRouteError的两个可用场景由源码 JSDoc 的标注(@mode framework、@mode data)直接给出:
- framework 模式:在路由模块导出的
ErrorBoundary组件中读取错误; - data 模式:配合数据路由对象上的
errorElement(或渲染级边界)读取当前路由的错误。
它读取的对象是在路由加载、action提交或组件渲染阶段抛出的异常,因此只能在这些错误边界渲染的上下文中调用,而不是任意组件都能随意使用。以最典型的路由模块ErrorBoundary为例:
export function ErrorBoundary() { const error = useRouteError(); return <div>{error.message}</div>; }当某条路由的loader、action抛错,或该路由组件在渲染中抛错时,React Router 不再渲染该路由组件,而是改渲染这个ErrorBoundary,把错误交给useRouteError()返回。
函数签名与返回值
function useRouteError(): unknown返回值在官方文档中定义如下:在路由loader、action执行或渲染期间抛出的错误。需要特别注意两点:
- 返回类型是
unknown,调用方必须先做类型收敛(instanceof Error、isRouteErrorResponse(error)等)再访问字段; - 若当前路由/错误边界内没有任何错误,通常返回
undefined。
这样的设计刻意避免了把错误类型臆断为某个具体类——因为 loader/action 中可以抛出任意值:普通Error、带status的Response(会被规范化为ErrorResponse),甚至任意字面量。
在路由模块 ErrorBoundary 中落地(framework 模式)
路由模块文档 docs/start/framework/route-module.md 给出了这一用法的完整骨架,其核心事实是:
当其他路由模块 API(如
loader、action)抛错时,路由模块ErrorBoundary会代替路由组件渲染。
一个“可直接复制”的健壮版错误边界如下,它用isRouteErrorResponse区分“HTTP 语义错误”与普通运行时异常:
import { isRouteErrorResponse, useRouteError, } from "react-router"; export function ErrorBoundary() { const error = useRouteError(); if (isRouteErrorResponse(error)) { return ( <div> <h1> {error.status} {error.statusText} </h1> <p>{error.data}</p> </div> ); } else if (error instanceof Error) { return ( <div> <h1>Error</h1> <p>{error.message}</p> <p>The stack trace is:</p> <pre>{error.stack}</pre> </div> ); } else { return <h1>Unknown Error</h1>; } }三段分支分别对应:
| 分支 | 错误来源示例 | 可访问字段 |
|---|---|---|
isRouteErrorResponse(error) | loader/action抛出Response(如throw new Response("Not Found", { status: 404 })),被内部规范化为ErrorResponse | error.status、error.statusText、error.data |
error instanceof Error | loader抛出的普通Error对象、组件渲染异常 | error.message、error.stack |
| 其它(兜底) | 抛出的字符串、undefined等非标准值 | —— |
对ErrorResponse判定函数isRouteErrorResponse的详细语义见 docs/api/utils/isRouteErrorResponse.md。
data 模式:errorElement 中的同一入口
useRouteError并不只属于 framework 模式。在 data 模式下,数据路由对象通过errorElement为某条路由声明错误边界,而该组件内部依然借助useRouteError()取错。仓库的集成测试大量印证了这一对应关系,例如在 packages/react-router/tests/dom/data-browser-router-test.tsx 中,错误边界组件内部均是:
let error = useRouteError() as ErrorResponse; // 或配合 isRouteErrorResponse(error) 分支渲染 status/statusText也就是说:无论你用的是 createBrowserRouter/createMemoryRouter 等数据路由器的errorElement,还是 framework 约定的模块级ErrorBoundary,取错入口统一都是useRouteError。两者唯一的差异是错误边界由谁承载、错误被记在哪一层。
源码解读:错误究竟从哪里来
useRouteError的实现位于 packages/react-router/lib/hooks.tsx,一共只有十余行:
export function useRouteError(): unknown { let error = React.useContext(RouteErrorContext); let state = useDataRouterState(DataRouterStateHook.UseRouteError); let routeId = useCurrentRouteId(DataRouterStateHook.UseRouteError); // If this was a render error, we put it in a RouteError context inside // of RenderErrorBoundary if (error !== undefined) { return error; } // Otherwise look for errors from our data router state return state.errors?.[routeId]; }从源码结构看,错误有两个来源通道,useRouteError按优先级依次查找:
渲染期错误(Render Error)——来自 React Context:
RouteErrorContext定义于 packages/react-router/lib/context.ts。当组件渲染抛错时,React Router 内部类组件RenderErrorBoundary(hooks.tsx)通过static getDerivedStateFromError(error)捕获异常,并在render()中把错误写入<RouteErrorContext.Provider value={error}>(hooks.tsx),随后渲染传入的错误边界组件,于是边界内调用useRouteError()即可从 Context 直接取出该错误。数据层错误(loader/action Error)——来自数据路由状态:
action、loader抛出的错误并不会触发 React 的渲染异常捕获,而是被数据路由器记录在全局状态state.errors(按routeId索引)。因此useRouteError还需要通过useDataRouterState拿到状态,再取state.errors?.[routeId]。
RenderErrorBoundary源码还透露了两处值得注意的行为:
- 错误会随 location 变化而重置:
getDerivedStateFromProps中,当state.location与新的props.location不一致(或一次 revalidation 从非 idle 回到 idle)时,会以新 props 的错误重置状态(hooks.tsx),从而保证用户“后退/前进”到无错路由时能自动从错误页恢复。 - RSC digest 会被解码:在 RSC 场景下,若错误对象携带字符串类型的
digest字段,渲染前会先尝试decodeRouteErrorResponseDigest(error.digest)还原出真实错误(hooks.tsx)。
此外,在 components 侧,错误边界组件会被withErrorBoundaryProps包装,统一注入params、loaderData、actionData与error(即useRouteError()的返回值),见 packages/react-router/lib/components.tsx——这说明useRouteError也正是框架级默认错误边界实现读取错误的底层入口。
使用边界与注意事项
综合官方文档与源码,使用时有几条需要记牢的约束:
- 只能在错误边界内调用:
useRouteError依赖RouteErrorContext或state.errors[routeId],脱离边界调用通常拿不到任何错误(返回undefined),没有实际意义。 - 不要假定返回类型:签名是
(): unknown,访问error.message、error.status前必须完成类型收敛(isRouteErrorResponse、instanceof Error、或对 RSC 场景额外检查error.data)。 - framework 与 data 模式语义一致但承载不同:framework 模式绑定模块导出
ErrorBoundary,data 模式绑定路由对象errorElement;不要把在普通页面组件中调用它当成“全局取错”的手段。 - 抛
Response能拿到 HTTP 语义:在loader/action里throw new Response(...)或使用throw redirect(...)等,错误边界中用isRouteErrorResponse判断后即可渲染status/statusText/data,这也是实现“404 / 500 页面”的推荐姿势。
深入阅读
- 本文档来源:docs/api/hooks/useRouteError.md
- 路由模块各 API 与
ErrorBoundary约定:docs/start/framework/route-module.md - 错误边界设计完整指南:docs/how-to/error-boundary.md
ErrorResponse判定与字段:docs/api/utils/isRouteErrorResponse.md- 实现源码:packages/react-router/lib/hooks.tsx、Context 定义 packages/react-router/lib/context.ts、渲染边界类 packages/react-router/lib/hooks.tsx
- 测试证据:data 模式错误边界使用样例见 packages/react-router/tests/dom/data-browser-router-test.tsx
【免费下载链接】react-routerDeclarative routing for React项目地址: https://gitcode.com/GitHub_Trending/re/react-router
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考