React 第五十五节 Router 中 useAsyncError的使用详解

发布于:2025-06-09 ⋅ 阅读:(12) ⋅ 点赞:(0)

前言

useAsyncErrorReact Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。

一、useAsyncError 用途

  1. 处理异步错误:捕获在 loaderaction 中发生的异步错误
  2. 替代传统错误边界:提供更细粒度的错误处理机制
  3. 局部错误处理:在组件级别处理错误而不影响整个应用
  4. 简化错误恢复:提供重试机制,方便用户重新尝试操作

二、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 使用场景

  1. 数据加载错误处理:当从API加载数据失败时显示友好的错误信息
  2. 表单提交错误:处理表单提交到服务器的错误响应
  3. 资源加载失败:如图片、文件等资源加载失败时的处理
  4. 条件性重试:提供特定操作的重试按钮
  5. 局部UI更新:在不刷新整个页面的情况下处理组件级错误

六、useAsyncError 最佳实践

  1. 提供有意义的错误信息:向用户显示可理解的错误描述
  2. 包含恢复操作:如重试按钮或返回链接
  3. 区分错误类型:根据错误类型显示不同的UI
  4. 记录错误:在控制台或日志服务中记录错误详情
  5. 优雅降级:在错误状态下显示替代内容

useAsyncError 提供了一种在组件级别处理异步错误的优雅方式,使我们能够创建更健壮、用户友好的应用程序。


网站公告

今日签到

点亮在社区的每一天
去签到