前言
useAsyncError
是 React Router v6.4
引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。
一、useAsyncError 用途
- 处理异步错误:捕获在
loader
或action
中发生的异步错误 - 替代传统错误边界:提供更细粒度的错误处理机制
- 局部错误处理:在组件级别处理错误而不影响整个应用
- 简化错误恢复:提供重试机制,方便用户重新尝试操作
二、useAsyncError 基本使用说明
import { useAsyncError } from 'react-router-dom';
function ErrorComponent() {
const error = useAsyncError();
return (
<div>
<h2>出错了!</h2>
<p>{error.message}</p>
{/* 提供重试按钮 */}
</div>
);
}
三、useAsyncError 完整代码示例
import React from 'react';
import {
createBrowserRouter,
RouterProvider,
useRouteError,
useAsyncError,
useLoaderData,
Await,
defer
} from 'react-router-dom';
// 模拟异步数据加载函数
const fetchData = async () => {
// 模拟网络延迟
await new Promise(resolve => setTimeout(resolve, 1000));
// 模拟50%的失败率
if (Math.random() > 0.5) {
throw new Error('数据加载失败:服务器响应超时');
}
return {
title: 'React Router 高级功能',
content: 'useAsyncError 允许您在组件级别处理异步错误...',
stats: { views: 245, likes: 32 }
};
};
// 加载器函数
export async function loader() {
return defer({
// 注意:这里不等待 Promise 解决
data: fetchData()
});
}
// 主内容组件
function DataContent() {
const { data } = useLoaderData();
return (
<div className="content">
<React.Suspense fallback={<div className="loading">加载中...</div>}>
<Await
resolve={data}
errorElement={<AsyncErrorBoundary />}
>
{(resolvedData) => (
<div className="data-card">
<h2>{resolvedData.title}</h2>
<p>{resolvedData.content}</p>
<div className="stats">
<span>👁️ {resolvedData.stats.views} 次查看</span>
<span>❤️ {resolvedData.stats.likes} 个赞</span>
</div>
</div>
)}
</Await>
</React.Suspense>
</div>
);
}
// 异步错误边界组件
function AsyncErrorBoundary() {
const error = useAsyncError();
return (
<div className="error-card">
<div className="error-header">
<span className="error-icon">⚠️</span>
<h3>加载数据时出错</h3>
</div>
<p className="error-message">{error.message}</p>
<button
className="retry-btn"
onClick={() => window.location.reload()}
>
重试
</button>
</div>
);
}
// 全局错误边界
function RootBoundary() {
const error = useRouteError();
return (
<div className="global-error">
<h1>应用遇到问题</h1>
<p>{error.message || error.statusText}</p>
<button
className="home-btn"
onClick={() => window.location = '/'}
>
返回首页
</button>
</div>
);
}
// 主应用组件
function App() {
return (
<div className="app">
<header>
<h1>React Router 错误处理演示</h1>
</header>
<main>
<DataContent />
</main>
<footer>
<p>尝试刷新页面,有50%几率看到错误处理效果</p>
</footer>
</div>
);
}
// 创建路由
const router = createBrowserRouter([
{
path: '/',
loader: loader,
element: <App />,
errorElement: <RootBoundary />
}
]);
// 应用入口
export default function Root() {
return <RouterProvider router={router} />;
}
// 将样式注入到文档中
const styleSheet = document.createElement('style');
styleSheet.innerText = styles;
document.head.appendChild(styleSheet);
四、useAsyncError 关键点解释
4.1、 数据加载模拟
const fetchData = async () => {
await new Promise(resolve => setTimeout(resolve, 1000));
if (Math.random() > 0.5) {
throw new Error('数据加载失败:服务器响应超时');
}
return { ... }; // 成功数据
};
这个函数模拟了50%失败率的异步数据加载,用于演示错误处理。
4.2、加载器配置
export async function loader() {
return defer({
data: fetchData()
});
}
使用 defer
延迟数据加载,允许组件在数据加载完成前渲染。
4.3、 主组件结构
function DataContent() {
const { data } = useLoaderData();
return (
<React.Suspense fallback={<div>加载中...</div>}>
<Await
resolve={data}
errorElement={<AsyncErrorBoundary />}
>
{/* 成功渲染的内容 */}
</Await>
</React.Suspense>
);
}
useLoaderData
获取加载器返回的数据
Suspense
处理加载状态
Await
组件处理异步数据解析
errorElement
指定错误时渲染的组件
4.4、 使用 useAsyncError
function AsyncErrorBoundary() {
const error = useAsyncError();
return (
<div className="error-card">
<p>{error.message}</p>
<button onClick={() => window.location.reload()}>
重试
</button>
</div>
);
}
useAsyncError
钩子从最近的 Await
组件中获取错误对象,用于显示错误信息和提供恢复操作。
4.5、 全局错误边界
function RootBoundary() {
const error = useRouteError();
return (
<div>
<h1>应用遇到问题</h1>
<p>{error.message}</p>
</div>
);
}
useRouteError
用于处理路由级别的错误(如加载器中的同步错误)。
五、useAsyncError 使用场景
- 数据加载错误处理:当从API加载数据失败时显示友好的错误信息
- 表单提交错误:处理表单提交到服务器的错误响应
- 资源加载失败:如图片、文件等资源加载失败时的处理
- 条件性重试:提供特定操作的重试按钮
- 局部UI更新:在不刷新整个页面的情况下处理组件级错误
六、useAsyncError 最佳实践
- 提供有意义的错误信息:向用户显示可理解的错误描述
- 包含恢复操作:如重试按钮或返回链接
- 区分错误类型:根据错误类型显示不同的UI
- 记录错误:在控制台或日志服务中记录错误详情
- 优雅降级:在错误状态下显示替代内容
useAsyncError
提供了一种在组件级别处理异步错误的优雅方式,使我们能够创建更健壮、用户友好的应用程序。