返回顶部
首页 > 资讯 > 前端开发 > JavaScript >React如何优雅的捕获异常
  • 466
分享到

React如何优雅的捕获异常

2024-04-02 19:04:59 466人浏览 八月长安
摘要

目录前言 ErrorBoundary Error Boundary 之外 try/catch window.onerror , error事件 unhandledrejection

前言

人无完人,所以代码总会出错,出错并不可怕,关键是怎么处理。
我就想问问大家React的应用的错误怎么捕捉呢? 这个时候:

  • 小白+++:怎么处理?
  • 小白++: ErrorBoundary
  • 小白+: ErrorBoundary, try catch
  • 小黑#:  ErrorBoundary, try catch, window.onerror
  • 小黑##: 这个是个严肃的问题,我知道N种处理方式,你有什么更好的方案?

ErrorBoundary

EerrorBoundary是16版本出来的,有人问那我的15版本呢,我不听我不听,反正我用16,当然15有unstable_handleError

关于ErrorBoundary官网介绍比较详细,这个不是重点,重点是他能捕捉哪些异常。

  • 子组件的渲染
  • 生命周期函数
  • 构造函数
  • class ErrorBoundary extends React.Component {

  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  componentDidCatch(error, info) {
    // Display fallback UI
    this.setState({ hasError: true });
    // You can also log the error to an error reporting service
    logErrorToMyService(error, info);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>

开源世界就是好,早有大神封装了react-error-boundary 这种优秀的库。
你只需要关心出现错误后需要关心什么,还以来个 Reset, 完美。


import {ErrorBoundary} from 'react-error-boundary'

function ErrorFallback({error, resetErrorBoundary}) {
  return (
    <div role="alert">
      <p>Something went wrong:</p>
      <pre>{error.message}</pre>
      <button onClick={resetErrorBoundary}>Try again</button>
    </div>
  )
}

const ui = (
  <ErrorBoundary
    FallbackComponent={ErrorFallback}
    onReset={() => {
      // reset the state of your app so the error doesn't happen again
    }}
  >
    <ComponentThatMayError />
  </ErrorBoundary>
)

遗憾的是,error boundaries并不会捕捉这些错误:

  • 事件处理程序
  • 异步代码 (e.g. setTimeout or requestAnimationFrame callbacks)
  • 服务端的渲染代码
  • error boundaries自己抛出的错误

原文可见参见官网introducing-error-boundaries

本文要捕获的就是 事件处理程序的错误。
官方其实也是有方案的how-about-event-handlers, 就是 try catch.

但是,那么多事件处理程序,我的天,得写多少,。。。。。。。。。。。。。。。。。。。。


  handleClick() {
    try {
      // Do something that could throw
    } catch (error) {
      this.setState({ error });
    }
  }

Error Boundary 之外

我们先看看一张表格,罗列了我们能捕获异常的手段和范围。

异常类型 同步方法 异步方法 资源加载 Promise async/await
try/catch
window.onerror
error
unhandledrejection

try/catch

可以捕获同步和async/await的异常。

window.onerror , error事件


    window.addEventListener('error', this.onError, true);
    window.onerror = this.onError

window.addEventListener('error') 这种可以比 window.onerror 多捕获资源记载异常.
请注意最后一个参数是 true, false的话可能就不如你期望。
当然你如果问题这第三个参数的含义,我就有点不想理你了。拜。

unhandledrejection

请注意最后一个参数是 true。


window.removeEventListener('unhandledrejection', this.onReject, true)

其捕获未被捕获的Promise的异常。

XMLHttpRequest 与 fetch

XMLHttpRequest 很好处理,自己有onerror事件。
当然你99.99%也不会自己基于XMLHttpRequest封装一个库, axiOS 真香,有这完毕的错误处理机制。
至于fetch, 自己带着catch跑,不处理就是你自己的问题了。
这么多,太难了。
还好,其实有一个库react-error-catch 是基于ErrorBoudary,error与unhandledrejection封装的一个组件。
其核心如下


   ErrorBoundary.prototype.componentDidMount = function () {
        // event catch
        window.addEventListener('error', this.catchError, true);
        // async code
        window.addEventListener('unhandledrejection', this.catchRejectEvent, true);
    };

使用:


import ErrorCatch from 'react-error-catch'

const App = () => {
  return (
  <ErrorCatch
      app="react-catch"
      user="cxyuns"
      delay={5000}
      max={1}
      filters={[]}
      onCatch={(errors) => {
        console.log('报错咯');
        // 上报异常信息到后端,动态创建标签方式
        new Image().src = `http://localhost:3000/log/report?info=${JSON.stringify(errors)}`
      }}
    >
      <Main />
    </ErrorCatch>)
}

export default

鼓掌,鼓掌。
其实不然: 利用error捕获的错误,其最主要的是提供了错误堆栈信息,对于分析错误相当不友好,尤其打包之后。
错误那么多,我就先好好处理React里面的事件处理程序。
至于其他,待续。

事件处理程序的异常捕获

示例

我的思路原理很简单,使用decorator来重写原来的方法。
先看一下使用:


   @methodCatch({ message: "创建订单失败", toast: true, report:true, log:true })
    async createOrder() {
        const data = {...};
        const res = await createOrder();
        if (!res || res.errCode !== 0) {
            return Toast.error("创建订单失败");
        }
        
        .......
        其他可能产生异常的代码
        .......
        
       Toast.success("创建订单成功");
    }

注意四个参数:

  • message: 出现错误时,打印的错误
  • toast: 出现错误,是否Toast
  • report: 出现错误,是否上报
  • log: 使用使用console.error打印

可能你说,这这,消息定死,不合理啊。我要是有其他消息呢。
此时我微微一笑别急, 再看一段代码


  @methodCatch({ message: "创建订单失败", toast: true, report:true, log:true })
    async createOrder() {
        const data = {...};
        const res = await createOrder();
        if (!res || res.errCode !== 0) {
            return Toast.error("创建订单失败");
        }
       
        .......
        其他可能产生异常的代码
        .......
        
       throw new CatchError("创建订单失败了,请联系管理员", {
           toast: true,
           report: true,
           log: false
       })
       
       Toast.success("创建订单成功");

    }

是都,没错,你可以通过抛出 自定义的CatchError来覆盖之前的默认选项。
这个methodCatch可以捕获,同步和异步的错误,我们来一起看看全部的代码。

类型定义


export interface CatchOptions {
    report?: boolean;
    message?: string;
    log?: boolean;
    toast?: boolean;
}

// 这里写到 const.ts更合理
export const DEFAULT_ERROR_CATCH_OPTIONS: CatchOptions = {
    report: true,
    message: "未知异常",
    log: true,
    toast: false
}

自定义的CatchError


import { CatchOptions, DEFAULT_ERROR_CATCH_OPTIONS } from "@typess/errorCatch";

export class CatchError extends Error {

    public __type__ = "__CATCH_ERROR__";
    
    constructor(message: string, public options: CatchOptions = DEFAULT_ERROR_CATCH_OPTIONS) {
        super(message);
    }
}

装饰器


import Toast from "@components/Toast";
import { CatchOptions, DEFAULT_ERROR_CATCH_OPTIONS } from "@typess/errorCatch";
import { CatchError } from "@util/error/CatchError";


const W_TYPES = ["string", "object"];
export function methodCatch(options: string | CatchOptions = DEFAULT_ERROR_CATCH_OPTIONS) {

    const type = typeof options;

    let opt: CatchOptions;

    
    if (options == null || !W_TYPES.includes(type)) { // null 或者 不是字符串或者对象
        opt = DEFAULT_ERROR_CATCH_OPTIONS;
    } else if (typeof options === "string") {  // 字符串
        opt = {
            ...DEFAULT_ERROR_CATCH_OPTIONS,
            message: options || DEFAULT_ERROR_CATCH_OPTIONS.message,
        }
    } else { // 有效的对象
        opt = { ...DEFAULT_ERROR_CATCH_OPTIONS, ...options }
    }

    return function (_target: any, _name: string, descriptor: PropertyDescriptor): any {

        const oldFn = descriptor.value;

        Object.defineProperty(descriptor, "value", {
            get() {
                async function proxy(...args: any[]) {
                    try {
                        const res = await oldFn.apply(this, args);
                        return res;
                    } catch (err) {
                        // if (err instanceof CatchError) {
                        if(err.__type__ == "__CATCH_ERROR__"){
                            err = err as CatchError;
                            const mOpt = { ...opt, ...(err.options || {}) };

                            if (mOpt.log) {
                                console.error("asyncMethodCatch:", mOpt.message || err.message , err);
                            }

                            if (mOpt.report) {
                                // TODO::
                            }

                            if (mOpt.toast) {
                                Toast.error(mOpt.message);
                            }

                        } else {
                            
                            const message = err.message || opt.message;
                            console.error("asyncMethodCatch:", message, err);

                            if (opt.toast) {
                                Toast.error(message);
                            }
                        }
                    }
                }
                proxy._bound = true;
                return proxy;
            }
        })
        return descriptor;
    }
}

总结一下

利用装饰器重写原方法,达到捕获错误的目的
自定义错误类,抛出它,就能达到覆盖默认选项的目的。增加了灵活性。


  @methodCatch({ message: "创建订单失败", toast: true, report:true, log:true })
    async createOrder() {
        const data = {...};
        const res = await createOrder();
        if (!res || res.errCode !== 0) {
            return Toast.error("创建订单失败");
        }
       Toast.success("创建订单成功");
       
        .......
        其他可能产生异常的代码
        .......
        
       throw new CatchError("创建订单失败了,请联系管理员", {
           toast: true,
           report: true,
           log: false
       })
    }

下一步

啥下一步,走一步看一步啦。
不,接下来的路,还很长。  这才是一个基础版本。

扩大成果


@XXXCatch
classs AAA{
    @YYYCatch
    method = ()=> {
    }
}

抽象,再抽象,再抽象

再见。

写在最后

error-boundaries
React异常处理
catching-react-errors
react进阶之异常处理机制-error Boundaries
decorator
core-decorators
autobind.js

到此这篇关于React如何优雅的捕获异常的文章就介绍到这了,更多相关React 捕获异常内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: React如何优雅的捕获异常

本文链接: https://lsjlt.com/news/128476.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • React如何优雅的捕获异常
    目录前言 ErrorBoundary Error Boundary 之外 try/catch window.onerror , error事件 unhandledrejection ...
    99+
    2024-04-02
  • ES7中await如何优雅的捕获异常详解
    目录传统方式await-to-js安装使用源码测试用例结束语本篇文章介绍的是ES7中await语法糖在调用的时候出现异常如何捕获? 传统方式 try { const res =...
    99+
    2022-12-12
    await 捕获异常 await 异常处理 await怎么捕获异常
  • Vue中如何优雅的捕获Promise异常详解
    目录常规的异常捕获方式好一些的方式:await-to-js更好的方式:全局捕获常规的异常捕获方式 在 Promise 提供了一个 .catch 方法用来捕获异常,假设有很多异步请求,...
    99+
    2022-11-13
    Vue Promise 异常捕获 Vue Promise
  • 如何在Java中捕获异常
    今天就跟大家聊聊有关如何在Java中捕获异常,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。Java的优点是什么1. 简单,只需理解基本的概念,就可以编写适合于各种情况的应用程序;2....
    99+
    2023-06-14
  • 如何动态捕获Python异常
    如何动态捕获Python异常,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。在讨论动态捕获异常时让我大吃一惊的是,可以让我找到隐藏的Bug和乐趣。有问题的代码下面...
    99+
    2023-06-17
  • java空指针异常如何捕获
    在Java中,可以使用try-catch语句块来捕获空指针异常(NullPointerException)。以下是一个简单的例子: ...
    99+
    2024-02-29
    java
  • 小程序中怎么优雅的捕捉异步方法的异常
    小编给大家分享一下小程序中怎么优雅的捕捉异步方法的异常,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!传统方法在ES7之后,我们往往使用 async await 语...
    99+
    2023-06-22
  • Python编程中如何捕获警告ps不是捕获异常
    目录1.警告不是异常2.警告能捕获吗3.捕获警告方法一4.捕获警告方法二5.捕获警告方法三1. 警告不是异常 你是不是经常在使用一些系统库或者第三方模块的时候,会出现一些既不是异常也...
    99+
    2024-04-02
  • PHP 中如何处理未捕获的异常?
    php 中处理未捕获的异常主要有两种方法:使用 set_exception_handler() 函数指定一个异常处理函数。注册 error_handler() 函数,它可以在未捕获的异常...
    99+
    2024-05-10
    php 异常
  • 异常处理:PHP中如何捕获和处理异常?
    异常处理:PHP中如何捕获和处理异常?在PHP开发中,异常处理是非常重要的一环。当程序发生意外情况或错误时,我们需要通过捕获和处理异常来保证程序的正常运行。PHP中提供了一套异常处理的机制,本文将介绍如何在PHP中捕获和处理异常,并提供具体...
    99+
    2023-12-18
    捕获 处理 异常
  • 详解python的异常捕获
    目录①捕捉一个异常②捕捉多个异常③Exception捕捉所有异常④raise主动触发异常⑤try…except…else…finally逻辑...
    99+
    2024-04-02
  • Python多层异常的捕获
    参考: 1、https://mozillazg.github.io/2016/08/python-the-right-way-to-catch-exception-then-reraise-another-exception.h...
    99+
    2023-01-31
    多层 异常 Python
  • SqlServer中存储过程如何捕获异常
    这期内容当中小编将会给大家带来有关SqlServer中存储过程如何捕获异常,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。  SqlServer中的存储过程怎么捕获异常  ...
    99+
    2024-04-02
  • java捕获异常后如何继续执行
    在Java中,可以使用try-catch语句来捕获异常并处理它们。一旦异常被捕获,程序会跳转到catch块中执行相应的代码,然后继续...
    99+
    2023-09-15
    java
  • Python3的异常捕获和处理
    1.try 和 except 这是异常语句。使用了这个可以对报错的代码,也会继续 执行下去而不会报错,不执行后面的代码。try是捕获异常,在try里的代码执行如果出错后,就会执行在execpt里的代码。try: print(2/0)...
    99+
    2023-01-31
    异常
  • springboot如何自定义异常并捕获异常返给前端
    小编给大家分享一下springboot如何自定义异常并捕获异常返给前端,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!背景在开发中,如果用try catch的方式,...
    99+
    2023-06-25
  • 如何调试 PHP 函数中未捕获的异常?
    如何调试 php 函数中未捕获的异常?使用 xdebug.scream:启用 xdebug 扩展的 xdebug.scream 配置选项,未捕获的异常时会显示蓝色屏幕和详细错误信息。使用...
    99+
    2024-04-17
    php 异常
  • PHP 函数中如何处理未捕获的异常?
    在 php 函数中处理未捕获的异常至关重要,可防止脚本出现致命错误。处理方法包括:使用 try-catch 块捕获异常并提供处理逻辑。使用 set_exception_handler()...
    99+
    2024-04-26
    php 异常
  • C++ 函数异常处理中如何捕获特定类型的异常?
    c++++ 中捕获特定类型异常的方法:使用 try-catch 块。在 catch 子句中指定要捕获的异常类型,如 catch (const std::runtime_error&...
    99+
    2024-04-15
    异常处理 特定类型异常 c++
  • PHP框架中的错误处理机制:优雅捕获异常,保障应用稳定性
    php 框架提供了异常处理机制,通过 try-catch 语句优雅地捕获错误,确保应用程序稳定性。大多数框架提供默认异常处理,捕获未处理异常并显示错误消息。自定义异常类可用于在 catc...
    99+
    2024-05-23
    错误处理 异常捕获 laravel
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作