如何在React应用中建了错误处理层

写在前面

在实际的业务场景中,react应用一定会涉及到错误处理的情况。默认情况下,react应用出现fetal error时,浏览器上显示的是一个空白页面,这对于用户来说很不友好---------他们并不知道发生了什么以及为什么发生。对于前端开发者而言,我们应该如何去处理这种错误处理,避免影响用户体验呢?最简单最有效的方法,就是在我们的react应用中添加一个错误处理层(Error Handling Layer)。

错误处理层能够将所有错误处理逻辑集中在一起,允许我们捕获错误并向用户显示清晰而有用的错误消息。这可以帮助react应用避免因为崩溃而导致的浏览器白屏显示,从而改善整体用户体验。

接下来就让我们详细了解错误处理层是什么,理解为什么React应用需要有一个错误处理层,以及如何在React中实现错误处理层。

react应用中什么是错误处理层?

错误处理层是react应用中的一部分,它将错误处理逻辑的代码封装并集中在一个地方,负责检测和处理react应用中发送的错误。

在React应用中,错误处理层通常负责以下功能:

  • 错误检测:拦截react应用程序在执行期间发生的错误和异常。
  • 错误处理:在检测到错误后,它将优雅的处理掉这些错误,特别是防止react应用程序因为一些错误导致崩溃而在浏览器端显示白屏,并且能够在浏览器端向用户显示自定义的错误页面。在背后,错误处理层还可以记录错误并将错误日志报告给错误监控服务。

react应用中为什么需要错误处理层?

如前文所述,react应用中默认对异常错误的处理方式就是卸载整个应用,浏览器显示白屏,它没有内置的机制来处理这些运行过程中发生的错误。可以想象,用户遇到应用白屏的情况,绝对会是一种懵逼状态,他们完全不知道发生了什么。因此,react默认的错误处理方式不是一个理想的解决方案,我们才需要在react应用中建立一个错误处理层。

详细地来说,建立错误处理层有以下好处:

  • 避免崩溃:如果有错误发生了,错误处理层会拦截它,并按照我们的意愿来处理错误,防止应用程序因未处理的错误而崩溃。
  • 改善用户体验:错误处理层无论何时检测到错误,最终都会让应用向用户显示一个漂亮的错误页面,并展示错误信息,这对用户来说十分友好。
  • 集中错误处理逻辑:能够将react应用中所有的错误处理逻辑集中在一起,并在react应用中集成错误日志记录和监控,开发者们可以方便的收集这些错误信息,然后使用这些信息来识别错误的根源、调试和修复错误。

下面我们就来看看如何在react应用中添加错误处理层。

使用错误边界(Error Boundary)来实现错误处理层

从react 16开始,官方提供了Error Boundary来进行错误处理。Error Boundary是一个特殊的react组件,它可以捕获子组件树中任何地方发生的JS错误,并且在错误发生时显示备用UI组件,从而优雅地处理错误并避免应用出现致命的崩溃。

需要注意的是,标准Error Boundary只能捕获在组件渲染时、生命周期方法中和组件构造函数中的错误,其它时候的错误它将捕获不到。

接下来让我们一步一步在React应用中使用Error Boundary来创建错误处理层!

1. 创建一个Error Boundary组件

要在React中创建一个Error Boundary,我们需要定义一个类组件,它包括以下内容:

  • 状态变量hasError:用于确定错误边界是否拦截了错误。
  • 静态方法getDerivedStateFromError:一个React生命周期方法,在后代组件抛出错误后调用。该方法返回的值将更新Error Boundary组件的状态。
  • 方法componentDidCatch:一个特殊的React生命周期方法,当Error Boundary捕获错误时调用。我们可以用它来记录错误日志并上报。
  • 一个带有if-else逻辑的render()方法:在出现错误的情况下,它返回备用UI组件。否则,它将显示由Error Boundary包装的子组件。

注意,React只在类组件上支持关键的生命周期方法getDerivedStateFromError()和componentDidCatch()。这意味着无法将错误边界编写为函数式组件。

定义Error Boundary组件:

js 复制代码
import React from "react";
import ErrorPage from "./ErrorPage";

export default class StandardErrorBoundary extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            hasError: false,
            error: undefined
        };
    }

    // 当出现错误时更新组件状态
    static getDerivedStateFromError(error) {
        // 指定Error Boundary捕获错误
        return {
            hasError: true,
            error: error
        };
    }

    // 定义捕获错误时要做什么
    componentDidCatch(error, errorInfo) {
        // 记录错误日志
        console.log("Error caught!");
        console.error(error);
        console.error(errorInfo);
    }

    render() {
        // 错误出现时ErrorPage
        if (this.state.hasError) {
            return <ErrorPage />;
        } else {
            // 正常显示
            return this.props.children;
        }
    }
}

当在它的任何子组件中发生错误时,StandardErrorBoundary会拦截、记录它,并将hasError设置为true,然后呈现备用UI组件。

定义ErrorPage 组件:

js 复制代码
export default function ErrorPage(props) {
    return (
        <div className={"error-page"}>
            <div className={"oops"}>Oops!</div>
            <div className={"message"}>Something went wrong...</div>
        </div>
    );
}

然后我们可以根据自己的喜好来定制备用UI组件ErrorPage样式就行了。值得注意的是,一个有效的ErrorPage组件告诉用户发生了什么就行了,不需要过多的细节来让用户担心。

2. 如何使用Error Boundary组件

我们在react应用中用上面定义的StandardErrorBoundary组件包裹住顶级组件就行了:

js 复制代码
<StandardErrorBoundary>
    <App />
</StandardErrorBoundary>

现在,react应用中发生的任何与react相关的错误都将被Error Boundary拦截和处理。

注意:react应用中可以有多个Error Boundary,每一个Error Boundary都负责处理不同组件的子树中的错误。但是为了将错误逻辑集中在一个地方,还是建议只使用一个顶级错误边界比较好。

3. Error Boundary的局限性

Error Boundary是一个很好的工具,但是它也有一些局限性,react官方文档中提到,Error Boundary不能捕获到下列的异常错误:

  • 异步代码
  • 事件处理函数
  • 服务器组件
  • Error Boundary自身

考虑到JavaScript严重依赖事件回调和异步代码,Error Boundary的这些限制有可能带来一些问题。

使用react-error-boundary来实现错误处理层

在react官方文档中,也推荐使用react-error-boundary来实现react应用中的错误处理。react-error-boundary是一个npm react库,它提供了一种简单可靠的方法来使用错误边界来处理错误。它简化了创建错误边界的过程,并提供了有效的解决方案来克服标准错误边界的局限性。

我们通过npm install 将react-error-boundary添加到我们的项目中来

js 复制代码
npm install react-error-boundary

接下来我详细讲讲如何使用它。

1. 创建Error Boundary组件

react-error-boundary库提供了一个ErrorBoundary组件,它暴露了几个prop属性来构建错误边界组件。下面是用react-error-boundary创建的错误边界:

js 复制代码
import ErrorPage from "./ErrorPage";
import { ErrorBoundary } from "react-error-boundary";

export default function ReactErrorBoundary(props) {
  return (
    <ErrorBoundary
      FallbackComponent={ErrorPage}
      onError={(error, errorInfo) => {
        // log the error
        console.log("Error caught!");  
        console.error(error);  
        console.error(errorInfo);
        // record the error in an APM tool...
      }}
    >
      {props.children}
    </ErrorBoundary>
  );
}

FallbackComponent属性,提供了一个在错误时渲染的备用UI组件。当错误发生时,也会触发onError事件,执行我们定义的事件处理函数。如果没有错误发生,将显示它所包裹的子组件。相比前面用官方的标准的Error Boundary组件,通过第三方库来创建错误边界,代码更简洁明了。

和前文一样,我们用我们的错误边界组件包裹我们顶级组件就行了:

js 复制代码
<ReactErrorBoundary>
    <App />
</ReactErrorBoundary>

用react-error-boundary构建的错误边界组件既可以是函数组件,也可以是类组件。我们不再局限于类组件,可以根据自己的目标和偏好做出最佳选择。

2. 发生错误后重置应用

react-error-boundary 提供了一个onReset的prop属性,它接受一个回调函数,ErrorBoundary组件会自动将这个方法传递给备用UI组件。

现在我们来更新我们前文定义的ReactErrorBoundary组件:

js 复制代码
import ErrorPage from "./ErrorPage";
import { ErrorBoundary } from "react-error-boundary";

export default function ReactErrorBoundary(props) {
  return (
    <ErrorBoundary
      FallbackComponent={ErrorPage}
      onError={(error, errorInfo) => {
        // log the error
        console.log("Error caught!");  
        console.error(error);  
        console.error(errorInfo);
      }}
      onReset={() => {
        // reloading the page to restore the initial state
        // of the current page
        console.log("reloading the page...");
        window.location.reload();
        // other reset logic...
      }}
    >
      {props.children}
    </ErrorBoundary>
  );
}

onReset回调函数所做的只是通过js重新加载当前页面,这是最基本的错误恢复逻辑。在更复杂的应用程序中,您可能需要将应用的状态恢复到前一个点或更新Redux状态。

默认情况下,ErrorBoundary会向备用UI组件传递以下两个prop:

  • Error:存储由错误边界捕获的错误对象。
  • resetErrorBoundary:包含传递给onReset prop属性的回调函数(如果定义的话)。

现在,我们可以试试添加一个重试按钮到ErrorPage,如下所示:

js 复制代码
import "./ErrorPage.css";
export default function ErrorPage(props) {
  return (
    <div className={"error-page"}>
      <div className={"oops"}>Oops!</div>
      <div className={"message"}>Something went wrong...</div>
      {props.resetErrorBoundary && (
        <div>
          <button className={"retry-button"} onClick={props.resetErrorBoundary}>
            Try Again!
          </button>
        </div>
      )}
    </div>
  );
}

现在,出现错误时显示的回退页面上将显示"Tra Again"按钮。当用户单击它时,将执行onReset函数并重新加载当前页面。如果应用是在用户操作过程中出现的错误,那么刷新页面,应用将恢复正常。

3. 处理异步代码的错误

react-error-boundary提供了useErrorHandler() hook,这个hook能够允许我们捕获到到传统的React错误边界无法捕获到的error,因此在API请求、事件处理程函数中,我们可以使用useErrorHandler()来捕获错误。

下面就是一个在异步请求代码中使用useErrorHandler() hook来捕获异常的示例:

js 复制代码
import { useEffect } from "react";
import { useErrorHandler } from "react-error-boundary";

export default function MyPageComponent(props) {
  const handleError = useErrorHandler();

  useEffect(() => {
    // API call
    fetchSomeData().then(() => {
      // ...
    }).catch((e) => {
      // 将错误传递给ErrorBoundary
      handleError(e);
    })
  }, [])

  // return ...
}

类似地,我们可以在标准错误边界捕获不到错误的任何情况下采用useErrorhandler()。另外,请记住,不管我们是用react-error-boundary实现的错误边界还是标准实现的错误边界,useErrorhandler()可以将错误传递过去

结语

在本文中,我们了解了错误处理层是什么,错误处理层的好处,以及如何将其集成到React应用中来。错误处理层其实就是一个错误边界组件,它能够捕获错误然后优雅地处理错误。这样我们就能够将错误处理相关的逻辑全部整合到react应用中一个地方,从而让我们调试、回溯问题更加方便容易。得益于第三方库react-error-boundary,我们能够弥补官方提供的标准错误边界组件的不足,让我们能够更加游刃有余的去实现react应用中错误处理层。

本文如有错误,敬请指正!

相关推荐
zqx_71 小时前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己1 小时前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
什么鬼昵称2 小时前
Pikachu-csrf-CSRF(get)
前端·csrf
长天一色2 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
NiNg_1_2342 小时前
npm、yarn、pnpm之间的区别
前端·npm·node.js
秋殇与星河2 小时前
CSS总结
前端·css
BigYe程普3 小时前
我开发了一个出海全栈SaaS工具,还写了一套全栈开发教程
开发语言·前端·chrome·chatgpt·reactjs·个人开发
余生H3 小时前
前端的全栈混合之路Meteor篇:关于前后端分离及与各框架的对比
前端·javascript·node.js·全栈
程序员-珍3 小时前
使用openapi生成前端请求文件报错 ‘Token “Integer“ does not exist.‘
java·前端·spring boot·后端·restful·个人开发
axihaihai3 小时前
网站开发的发展(后端路由/前后端分离/前端路由)
前端