前言
useRouteError
是 React Router v6.4+
引入的关键错误处理钩子,用于在 路由错误边界(Error Boundary
) 中获取路由操作过程中发生的错误信息。
它提供了优雅的错误处理机制,让开发者能够创建用户友好的错误界面。
一、useRouteError 核心用途
1.1、错误捕获:
获取路由加载、渲染或数据提交过程中发生的错误
1.2、错误展示:
在错误边界组件中显示有意义的错误信息
1.3、错误恢复:
提供错误恢复机制(如重试按钮)
1.4、错误分类:
根据错误类型显示不同的UI界面
二、useRouteError 基本用法示例
import { useRouteError } from 'react-router-dom';
function ErrorPage() {
const error = useRouteError();
return (
<div id="error-page">
<h1>哎呀!出错了</h1>
<p>抱歉,发生了一个意外的错误。</p>
<p>
<i>{error.statusText || error.message}</i>
</p>
</div>
);
}
三、useRouteError 实际应用场景
3.1、基本错误边界组件
import { useRouteError, isRouteErrorResponse } from 'react-router-dom';
export function ErrorBoundary() {
const error = useRouteError();
// 检查是否为路由错误响应
if (isRouteErrorResponse(error)) {
return (
<div className="error-container">
<h1 className="error-title">
{error.status} {error.statusText}
</h1>
<p className="error-message">{error.data}</p>
{error.status === 404 && (
<Link to="/" className="home-link">
返回首页
</Link>
)}
</div>
);
}
// 处理其他类型的错误
return (
<div className="error-container">
<h1 className="error-title">未知错误</h1>
<p className="error-message">
{error.message || '发生了一个未知错误'}
</p>
<button
className="retry-btn"
onClick={() => window.location.reload()}
>
重试
</button>
</div>
);
}
3.2、分层错误边界
// 根错误边界 - 应用级别错误
export function RootErrorBoundary() {
const error = useRouteError();
return (
<div className="fullscreen-error">
<div className="error-card">
<h1>应用错误</h1>
<p>抱歉,整个应用出现了问题</p>
<ErrorDetails error={error} />
<div className="actions">
<button onClick={() => window.location.reload()}>
刷新应用
</button>
<a href="/">返回首页</a>
</div>
</div>
</div>
);
}
// 路由级别错误边界
export function RouteErrorBoundary() {
const error = useRouteError();
return (
<div className="route-error">
<h2>页面错误</h2>
<ErrorDetails error={error} />
<div className="actions">
<button onClick={() => window.history.back()}>
返回上一页
</button>
</div>
</div>
);
}
// 错误详情组件
function ErrorDetails({ error }) {
return (
<div className="error-details">
<p><strong>错误信息:</strong> {error.message}</p>
{isRouteErrorResponse(error) && (
<p><strong>状态码:</strong> {error.status}</p>
)}
{error.stack && (
<details className="stack-trace">
<summary>查看技术细节</summary>
<pre>{error.stack}</pre>
</details>
)}
</div>
);
}
3.3、在路由配置中使用错误边界
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import {
RootLayout,
Dashboard,
UserProfile,
Settings,
RootErrorBoundary,
RouteErrorBoundary
} from './components';
const router = createBrowserRouter([
{
path: '/',
element: <RootLayout />,
errorElement: <RootErrorBoundary />, // 应用级错误边界
children: [
{
index: true,
element: <Dashboard />
},
{
path: 'profile/:userId',
element: <UserProfile />,
errorElement: <RouteErrorBoundary />, // 路由级错误边界
loader: userProfileLoader // 可能抛出错误的loader
},
{
path: 'settings',
element: <Settings />,
errorElement: <RouteErrorBoundary />,
action: settingsAction // 可能抛出错误的action
}
]
}
]);
function App() {
return <RouterProvider router={router} />;
}
3.4、自定义错误类型处理
import { useRouteError } from 'react-router-dom';
export function ApiErrorBoundary() {
const error = useRouteError();
// 处理API错误
if (error.name === 'ApiError') {
return (
<div className="api-error">
<h2>API 请求失败</h2>
<p>错误代码: {error.code}</p>
<p>{error.message}</p>
{error.code === 401 ? (
<button onClick={() => location.href = '/login'}>
重新登录
</button>
) : (
<button onClick={() => window.location.reload()}>
重试
</button>
)}
</div>
);
}
// 处理路由错误响应
if (isRouteErrorResponse(error)) {
switch (error.status) {
case 404:
return <NotFoundError error={error} />;
case 403:
return <ForbiddenError error={error} />;
case 500:
return <ServerError error={error} />;
default:
return <GenericHttpError error={error} />;
}
}
// 默认错误处理
return <GenericError error={error} />;
}
// 404错误组件
function NotFoundError({ error }) {
return (
<div className="not-found">
<h1>页面未找到</h1>
<p>您访问的页面不存在</p>
<p>错误信息: {error.data || error.statusText}</p>
<Link to="/" className="home-link">
返回首页
</Link>
</div>
);
}
// 403错误组件
function ForbiddenError({ error }) {
return (
<div className="forbidden">
<h1>权限不足</h1>
<p>您没有权限访问此内容</p>
<div className="actions">
<button onClick={() => window.history.back()}>
返回上一页
</button>
<Link to="/contact">联系管理员</Link>
</div>
</div>
);
}
3.5、结合加载器的错误处理
// 用户详情页loader
export async function userLoader({ params }) {
try {
const user = await fetchUser(params.userId);
if (!user) {
// 抛出404错误响应
throw new Response('用户未找到', {
status: 404,
statusText: 'Not Found'
});
}
return user;
} catch (error) {
// 抛出自定义API错误
throw new ApiError({
message: '加载用户数据失败',
code: error.status || 500,
originalError: error
});
}
}
// 自定义API错误类
export class ApiError extends Error {
constructor({ message, code, originalError }) {
super(message);
this.name = 'ApiError';
this.code = code;
this.originalError = originalError;
// 保留原始堆栈跟踪
if (originalError?.stack) {
this.stack = originalError.stack;
}
}
}
四、useRouteError 高级用法:错误恢复机制
import { useRouteError, useNavigate } from 'react-router-dom';
export function RecoverableErrorBoundary() {
const error = useRouteError();
const navigate = useNavigate();
const [retryCount, setRetryCount] = useState(0);
const handleRetry = () => {
setRetryCount(c => c + 1);
navigate('.'); // 重新加载当前路由
};
const handleBack = () => {
navigate(-1); // 返回上一页
};
return (
<div className="recoverable-error">
<h1>发生错误</h1>
<p>{error.message || '请稍后再试'}</p>
<div className="error-actions">
<button onClick={handleRetry} className="retry-btn">
重试 {retryCount > 0 && `(${retryCount})`}
</button>
<button onClick={handleBack} className="back-btn">
返回
</button>
</div>
{retryCount > 2 && (
<div className="advanced-options">
<p>多次重试失败?</p>
<button onClick={() => window.location.reload()}>
刷新整个页面
</button>
<Link to="/support">联系支持</Link>
</div>
)}
</div>
);
}
五、useRouteError 错误类型处理指南
错误类型 识别方法 推荐处理方式
404 Not Found
: error.status === 404; 显示"未找到"页面,提供返回首页链接
403 Forbidden
: error.status === 403; 显示权限不足信息,提供登录或联系管理员选项
500 Server Error
:error.status === 500;显示服务器错误,提供重试或联系支持选项
网络错误 :error instanceof TypeError; 显示连接问题,检查网络并提供重试
自定义错误: 自定义错误类; 根据业务逻辑定制错误处理
未捕获异常: 其他所有错误; 显示通用错误信息,提供技术细节
六、useRouteError 最佳实践
6.1、分层错误边界:
应用级:捕获整个应用的意外错误
路由级:处理特定路由的错误
组件级:处理局部UI错误
6.2、用户友好信息:
避免显示技术性错误信息给普通用户
提供清晰的恢复操作指引
为开发环境保留详细错误日志
6.3、错误恢复机制:
提供重试按钮
实现返回上一页功能
添加刷新页面选项
6.4、错误日志记录:
useEffect(() => {
if (error) {
logErrorToService(error);
}
}, [error]);
6.5、开发环境增强:
function ErrorDetails({ error }) {
return (
<div>
{process.env.NODE_ENV === 'development' && (
<details>
<summary>开发人员详情</summary>
<pre>{error.stack}</pre>
</details>
)}
</div>
);
}
七、useRouteError 注意事项
7.1、错误边界位置:
错误边界必须位于路由层级中
只能捕获其子组件树中的错误
7.2、错误类型检查:
使用 isRouteErrorResponse
检查路由错误响应
处理各种可能的错误类型
7.3、异步错误处理:
loader/action
中的异步错误会被自动捕获
组件中的错误需要使用 useRouteError
获取
7.4、错误传播:
错误会向上传播到最近的错误边界
未被捕获的错误会导致应用崩溃
7.5、与React错误边界结合:
class ReactErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
if (this.state.hasError) {
return <RouterErrorBoundary />;
}
return this.props.children;
}
}
总结
useRouteError
是 React Router v6.4+
错误处理机制的核心,可以帮助我们实现如下场景:
- 创建用户友好的错误界面
- 实现分层错误处理策略
- 根据错误类型提供定制化UI
- 实现错误恢复和重试机制
- 结合路由加载器和动作进行错误处理
通过合理使用 useRouteError
,我们可以显著提升应用的健壮性和用户体验,确保即使发生错误,用户也能获得清晰的反馈和恢复路径。