React 错误边界指南

简介: React 错误边界指南

React 错误边界指南

虽然在错误到达生产环境之前捕获错误是理想的,但是其中一些错误(例如网络错误)可能会通过测试而影响用户。如果你的 React 组件没有正确地捕捉到第三方库或 React Hooks 抛出的错误,这样的错误要么导致 React 生命周期崩溃,要么到达主执行线程的顶层,导致“白屏”场景:

在React 16中,没有捕捉到的错误[…]将导致整个 React 组件树被卸载

c78aa3fc5ad4dd32a84a4cdf562d79e1.png

您的应用程序通过提供适当的可视化反馈和潜在操作(例如:重试机制)来优雅地处理此类错误是至关重要的。

幸运的是,使用 React API 实现这样的用户体验模式只需要很少的工作,对于最高级的用户体验,还需要轻量级 React 库的帮助。

在 React Hooks 调用周围使用 JavaScript 的 try-catch 是行不通的,因为它们的执行是异步的。然而,React API 提供了错误边界机制来捕获组件中可能“冒出来”的所有类型的错误。

例如,如果 <ComponentA /> 被封装在一个 React Error 边界中,错误传播将在 Error boundary 级别停止,防止 React App 崩溃:

ad4e437f89fc36ecbb1cb2fabb6360f5.png

本文将介绍如何在应用程序中实现错误边界,从简单的错误捕获到显示可视化反馈和提供重试机制。

1. 简单错误边界的捕获和报告错误

在它复杂的名字背后,Error Boundary 只是一个实 componentDidCatch(error) 方法的普通类 React 组件:

class ErrorBoundarySimple extends React.Component {
   componentDidCatch(error) {
      // 报告错误到您最喜欢的错误跟踪工具(例如:Sentry, Bugsnag)
   }
   render() {
      return  this.props.children;
   }
}

注意:React 还没有提供基于 hook 的替代方法来实现错误边界。

一旦错误到达我们的 MyErrorBoundary 组件,componentDidCatch() 类方法就会被调用,这允许我们防止 React 应用程序崩溃并将错误转发到我们的错误报告工具。

让我们让 <ErrorBoundarySimple> 更加友好,在错误被抛出时添加简单的可视化反馈。为此,我们向 ErrorBoundarySimple 添加一些状态,并使用 getDerivedStateFromError() 方法,如下所示:

class ErrorBoundarySimple extends React.Component {
  state = { hasError: false };
  componentDidCatch(error: unknown) {
    // 报告错误到您最喜欢的错误跟踪工具(例如:Sentry, Bugsnag)
    console.error(error);
  }
  static getDerivedStateFromError(error: unknown) {
    // 更新状态,使下一次渲染将显示回退 UI 
    return { hasError: true };
  }
  render() {
    if (this.state.hasError) {
      return <p>Failed to fetch users.</p>;
    }
    return this.props.children;
  }
}

React 期望 getDerivedStateFromError() 方法在发生错误时返回应用于  <ErrorBoundarySimple> 的状态值,从而使我们的 UI 提供视觉反馈。

错误边界也可以嵌套,以提供更多上下文化的反馈。例如,在这个 React 应用树中,我们可能想根据崩溃的内容提供不同的反馈。例如,当聊天崩溃和 TodoList 崩溃时,我们可能希望提供不同的反馈,但仍然在应用程序级别处理任何类型的崩溃。我们可以引入多个边界来实现这一点:

a1c8121db8deb1afca50e1413b71e5d3.png

通过上面的设置,<Chat> 组件(或它的子组件)中的任何错误都将被捕获在包装 <Chat> 组件的错误边界(而不是“App”错误边界)中,允许我们给出上下文化的可视化反馈。但是,来自所有 <App> 后代的任何错误(不包括<Chat><TodoList>)将被" App "错误边界捕获。

仅用几行代码,我们就通过优雅地处理应用程序中的错误,极大地改善了用户体验。然而,这种简单的错误边界实现确实有局限性。

首先,根据 React 文档,错误边界不会捕获以下错误:

  • 事件处理
  • 异步代码(例如 setTimeoutrequestAnimationFrame 回调)
  • 服务器端渲染
  • 抛出在错误边界本身(而不是其子边界)中的错误

而且,前面展示的错误边界没有为用户提供从错误中恢复的任何操作,例如,通过重试机制。在下一节中,我们将了解如何利用 react-error-boundary 库来处理所有这些边界情况。

2. 高级错误边界的捕获所有错误和重试机制

现在,让我们通过捕捉各种错误并向用户公开恢复操作来提供高级的错误处理用户体验。为此,我们将使用 react-error-boundary 库,该库可以按如下方式安装:

npm install --save react-error-boundary
yarn add react-error-boundary

2.1 「提供重试机制」

我们新定义了一个 <Users> 组件,该组件在50%的情况下无法加载用户。

让我们使用 react-error-boundary 来正确捕获错误并提供重试机制:

import { ErrorBoundary, FallbackProps } from "react-error-boundary";
import { Users } from "./Users";
function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
  return (
    <div role="alert">
      <p>Failed to load users:</p>
      <pre>{error.message}</pre>
      <button onClick={resetErrorBoundary}>Try again</button>
    </div>
  );
}
export default function App(): JSX.Element {
  return (
    <div className="App">
      <ErrorBoundary FallbackComponent={ErrorFallback}>
        {/* Users 加载失败的概率为50% */}
        <Users />
      </ErrorBoundary>
    </div>
  );
}

<ErrorBoundary> 接受一个强制的 FallbackComponent = prop,它应该是发生错误时将呈现的 react  组件或 JSX。如果是一个组件,这个FallbackComponent=function 将接收 FallbackProps

  • error 可用于显示错误。
  • resetErrorBoundary 是一个回调函数,用于重置错误状态并重新渲染子组件。

还可以提供 ononError prop,将错误转发到您最喜欢的错误报告工具(例如:Sentry)。react-error-boundary「文档」 展示了如何利用其他props(例如:onReset=)来处理更高级的场景。

2.2 捕获所有的错误

如前所述,错误边界不会捕获以下错误:

  • 事件处理
  • 异步代码(例如 setTimeoutrequestAnimationFrame 回调)

因为这种错误发生在 React 呈现生命周期之外,所以不会调用 错误边界。同样,通过提供 handleError() hook 来帮助捕获与事件相关的和异步错误,庆幸的是 react-error-boundary已经给我们提供了。

import { useErrorHandler } from 'react-error-boundary'
function Greeting() {
  const [greeting, setGreeting] = React.useState(null)
  const handleError = useErrorHandler()
  function handleSubmit(event) {
    event.preventDefault()
    const name = event.target.elements.name.value
    fetchGreeting(name).then(
      newGreeting => setGreeting(newGreeting),
      error => handleError(error),
    )
  }
  return greeting ? (
    <div>{greeting}</div>
  ) : (
    <form onSubmit={handleSubmit}>
      <label>Name</label>
      <input id="name" />
      <button type="submit">get a greeting</button>
    </form>
  )
}

handleSubmit() 函数中发生的错误不会被 React 呈现生命周期捕获。因此,我们使用 React -error-boundaryuseErrorHandler() 提供的 handleError 函数在 React 生命周期中重新抛出错误,以便最近的 ErrorBoundary 可以捕获它。

3. 小结

React Error Boundary 是一种优雅地处理 React 应用程序中任何类型错误的直接方法。

好的产品应该防止错误到达生产,但也应该使用错误边界为用户提供上下文反馈和恢复操作,以防出现意外错误。


相关文章
|
Java
【IntelliJ IDEA】中文乱码问题 ( 代码乱码 | 编译乱码 | 控制台乱码 )
【IntelliJ IDEA】中文乱码问题 ( 代码乱码 | 编译乱码 | 控制台乱码 )
2578 0
【IntelliJ IDEA】中文乱码问题 ( 代码乱码 | 编译乱码 | 控制台乱码 )
|
存储 Docker 容器
Docker安装默认存储路径修改与镜像恢复
Docker安装默认存储路径修改与镜像恢复
561 0
|
索引 Python
Python 教程之 Pandas(10)—— 访问 series 的元素
Python 教程之 Pandas(10)—— 访问 series 的元素
332 0
Python 教程之 Pandas(10)—— 访问 series 的元素
|
2月前
|
编解码 人工智能 自然语言处理
牛B, 我去,新手小白也能使用InfiniteTalk搭建属于自己的数字人啦 ,真的太简单啦!!!
小华同学带你解锁AI高效工具!InfiniteTalk创新“稀疏帧配音”技术,实现口型、表情、身态协同演进,支持I2V/V2V双模式,确保长视频ID/背景稳定,流畅跨片段衔接,助力课程、宣传、电商等多场景降本增效。
806 6
|
4月前
|
搜索推荐 JavaScript Java
基于springboot的毕业旅游一站式定制系统
本系统基于Spring Boot、Vue等技术,构建毕业旅游一站式定制平台,整合旅游资源,利用大数据与人工智能实现个性化行程规划,满足毕业生多样化需求,提升旅游体验与行业效率。
|
IDE Java 分布式数据库
Apache HBase 落地JAVA 实战
Apache HBase 落地 Java 实战主要涉及使用 Java API 来操作 HBase 数据库,包括表的创建、删除、数据的插入、查询等操作。以下是一个基于 Java 的 HBase 实战指南,包括关键步骤和示例代码。
825 23
|
弹性计算 固态存储 大数据
阿里云服务器多少钱一年?2024年8月最新价格表连夜整理(收藏级)
阿里云服务器价格优惠,2024年最新租用费用显示轻量应用服务器2核2G3M带宽年费82元,2核4G4M带宽年费298元。新老用户共享99元一年的2核2G3M带宽ECS服务器,2核4G5M带宽ECS优惠价199元一年。游戏服务器方面,4核16G10M带宽每月70元,8核32G10M带宽每月160元。GPU服务器如gn6v、gn6i等也有相应折扣。此外,提供了不同配置的ECS实例,包括经济型e实例、通用算力型u1实例等,并附有按小时计费的价格表。公网带宽按固定带宽或流量计费,系统盘提供高效云盘、SSD云盘和ESSD云盘选项。详情及最新优惠请参见阿里云官方页面。
2649 0
|
机器学习/深度学习 人工智能 文字识别
轻松识别文字,这款Python OCR库支持超过80种语言
轻松识别文字,这款Python OCR库支持超过80种语言
1138 2
|
前端开发 JavaScript API
深入探讨 React 中 useDispatch 和 useSelector 的使用
深入探讨 React 中 useDispatch 和 useSelector 的使用
756 0
|
机器学习/深度学习 人工智能 数据挖掘
Python简史
Python简史
276 0