React 第五十三节 Router中 useRouteError 的使用详解和案例分析

news2025/6/6 19:40:26

前言

useRouteErrorReact 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;
  }
}

总结

useRouteErrorReact Router v6.4+ 错误处理机制的核心,可以帮助我们实现如下场景:

  1. 创建用户友好的错误界面
  2. 实现分层错误处理策略
  3. 根据错误类型提供定制化UI
  4. 实现错误恢复和重试机制
  5. 结合路由加载器和动作进行错误处理

通过合理使用 useRouteError,我们可以显著提升应用的健壮性和用户体验,确保即使发生错误,用户也能获得清晰的反馈和恢复路径。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2402037.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

攻防世界-XCTF-Web安全最佳刷题路线

每次写序都是最烦恼的&#xff0c;都不知道写什么&#xff0c;CTF是团队竞赛&#xff0c;有很多分支&#xff08;Web安全&#xff0c;密码学&#xff0c;杂项&#xff0c;Pwn&#xff0c;逆向&#xff0c;安卓&#xff09;&#xff0c;可以每个领域都涉猎&#xff0c;或许感觉那…

t021-高校物品捐赠管理系统【包含源码材料!!!!】

视频演示地址 摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装高校物品捐赠管理系统软件来发挥其高效地信息…

设计模式——面向对象设计六大原则

摘要 本文详细介绍了设计模式中的六大基本原则&#xff0c;包括单一职责原则、开放封闭原则、里氏替换原则、接口隔离原则、依赖倒置原则和合成复用原则。每个原则都通过定义、理解、示例三个部分进行阐述&#xff0c;旨在帮助开发者提高代码的可维护性和灵活性。通过具体代码…

Python制作史莱姆桌面宠物!可爱的

史莱姆桌面宠物 一个可爱的桌面史莱姆宠物&#xff0c;它会在您的任务栏上移动并提供可视化设置界面。 这里写目录标题 史莱姆桌面宠物功能特点安装与运行直接运行方式创建可执行文件 使用说明自定义GIF说明打包说明开源地址 功能特点 可爱的史莱姆在任务栏上自动移动支持…

Dify源码教程:账户和密码传递分析

概述 Dify系统中账户创建过程中的密码处理是Web应用安全的重要环节。本教程详细分析了从前端表单到后端存储的完整流程&#xff0c;展示了Dify如何安全地处理用户凭据。 前端部分 在 dify/web/app/install/installForm.tsx 文件中&#xff0c;当用户填写完表单并点击安装按钮…

数据分析图表类型及其应用场景

说明&#xff1a;顶部HTML文件下载后可以直接查看&#xff0c;带有示图。 摘要 数据可视化作为现代数据分析的核心环节&#xff0c;旨在将复杂、抽象的数据转化为直观、易懂的图形形式。这种转化显著提升了业务决策能力&#xff0c;优化了销售与营销活动&#xff0c;开辟了新…

Github 2025-06-03Python开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2025-06-03统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目10Rust项目1HTML项目1C项目1 系统设计指南 创建周期&#xff1a;2507 天开发语言&#xff1a;Pyt…

电脑提示dll文件缺失怎么办 dll修复方法

当你在使用某些应用程序或启动电脑时&#xff0c;看到提示“DLL文件缺失”的错误信息&#xff0c;这通常意味着某个必要的动态链接库&#xff08;DLL&#xff09;文件无法被找到或加载&#xff0c;导致软件无法正常运行。本文将详细介绍如何排查和修复DLL文件缺失的问题&#x…

【自动思考记忆系统】demo (Java版)

背景&#xff1a;看了《人工智能》中的一段文章&#xff0c;于是有了想法。想从另一种观点&#xff08;⭕️&#xff09;出发&#xff0c;尝试编码&#xff0c;告别传统程序员一段代码解决一个问题的方式。下图是文章原文和我的思考涂鸦✍️&#xff0c;于是想写一个自动思考记…

51单片机基础部分——独立按键检测

前言 在单片机开发中&#xff0c;我们会经常对单片机的状态进行控制&#xff0c;比如我们会控制某个灯点亮&#xff0c;某个灯熄灭&#xff0c;这个时候我们就要开始做控制&#xff0c;我们可以通过什么控制呢&#xff0c;这个地方我们选择按键控制 按键实物及工作原理 生活…

【Docker管理工具】部署Docker可视化管理面板Dpanel

【Docker管理工具】部署Docker可视化管理面板Dpanel 一、Dpanel介绍1.1 DPanel 简介1.2 主要特点 二、本次实践规划2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载Dpanel镜像五、部署Dpanel…

springboot实现查询学生

文章目录 数据库前端 请求mybatis 数据库 前端 请求 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body> <a href"/list">启动学生管理…

Appium+python自动化(九)- 定位元素工具

简介 环境搭建好了&#xff0c;其他方面的知识也准备的差不多了&#xff0c;那么就开始下一步元素定位&#xff0c;元素定位主要介绍如何使用uiautomatorviewer&#xff0c;通过定位到页面上的元素&#xff0c;然后进行相应的点击等操作. 此外在介绍另一款工具&#xff1a;Insp…

Unity 中实现可翻页的 PageView

之前已经实现过&#xff1a; Unity 中实现可复用的 ListView-CSDN博客文章浏览阅读5.6k次&#xff0c;点赞2次&#xff0c;收藏27次。源码已放入我的 github&#xff0c;地址&#xff1a;Unity-ListView前言实现一个列表组件&#xff0c;表现方面最核心的部分就是重写布局&…

云计算 Linux Rocky day05【rpm、yum、history、date、du、zip、ln】

云计算 Linux Rocky day05【rpm、yum、history、date、du、zip、ln】 目录 云计算 Linux Rocky day05【rpm、yum、history、date、du、zip、ln】1.RPM包的一般安装位置2.软件名和软件包名3.查询软件信息4.查询软件包5.导入红帽签名信息&#xff0c;解决查询软件包信息报错6.利用…

LuaJIT2.1 和 Lua5.4.8 性能对比

说明 最近在学习 LuaJIT&#xff0c;想看看把它接入到项目中使用&#xff0c;会提高多大的性能。 今天抽时间&#xff0c;简单地测试了一下 LuaJIT 2.2 和 Lua5.4.8 的性能。 测试平台&#xff1a; 系统&#xff1a;Windows 10 WSLCPU&#xff1a;Intel Core™ i7-8700 CPU…

深入解析与解决方案:处理Elasticsearch中all found copies are either stale or corrupt未分配分片问题

目录 引言 1 问题诊断深入分析 1.1 错误含义深度解析 1.2 获取详细的诊断信息 2 解决方案选择与决策流程 2.1 可用选项全面对比 2.2 推荐处理流程与决策树 3 具体操作步骤详解 3.1 优先尝试 - 分配最新副本&#xff08;最低风险&#xff09; 3.2 中等风险方案 - 分配…

【NLP 78、手搓Transformer模型结构】

你以为走不出的淤泥&#xff0c;也迟早会云淡风轻 —— 25.5.31 引言 ——《Attention is all you need》 《Attention is all you need》这篇论文可以说是自然语言处理领域的一座里程碑&#xff0c;它提出的 Transformer 结构带来了一场技术革命。 研究背景与目标 在 Transfo…

如何自定义WordPress主题(5个分步教程)

如果您已经安装了一个 WordPress 主题&#xff0c;但它不太适合您&#xff0c;您可能会感到沮丧。在定制 WordPress 主题方面&#xff0c;您有很多选择。 挑战在于找到正确的方法。 在本篇文章中&#xff0c;我将引导您了解自定义 WordPress 主题的各种选项&#xff0c;帮助您…

react实现markdown文件预览

文章目录 react实现markdown文件预览1、实现md文件预览2、解决图片不显示3、实现效果 react实现markdown文件预览 1、实现md文件预览 1️⃣第一步&#xff1a;安装依赖&#xff1a; npm install react-markdown remark-gfmreact-markdown&#xff1a;将 Markdown 渲染为 Rea…