最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

React如何優(yōu)雅的捕獲異常

 更新時間:2021年06月18日 10:07:23   作者:云的世界  
捕獲異常是來定位你錯誤代碼的。本文主要介紹了 React如何捕獲異常,你知道多少種方法,ErrorBoundary,ErrorBoundary-try-catch等等。本文就來詳細的介紹一下

前言

人無完人,所以代碼總會出錯,出錯并不可怕,關鍵是怎么處理。
我就想問問大家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 這種優(yōu)秀的庫。
你只需要關心出現錯誤后需要關心什么,還以來個 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('報錯咯');
        // 上報異常信息到后端,動態(tài)創(chuàng)建標簽方式
        new Image().src = `http://localhost:3000/log/report?info=${JSON.stringify(errors)}`
      }}
    >
      <Main />
    </ErrorCatch>)
}

export default

鼓掌,鼓掌。
其實不然: 利用error捕獲的錯誤,其最主要的是提供了錯誤堆棧信息,對于分析錯誤相當不友好,尤其打包之后。
錯誤那么多,我就先好好處理React里面的事件處理程序。
至于其他,待續(xù)。

事件處理程序的異常捕獲

示例

我的思路原理很簡單,使用decorator來重寫原來的方法。
先看一下使用:

   @methodCatch({ message: "創(chuàng)建訂單失敗", toast: true, report:true, log:true })
    async createOrder() {
        const data = {...};
        const res = await createOrder();
        if (!res || res.errCode !== 0) {
            return Toast.error("創(chuàng)建訂單失敗");
        }
        
        .......
        其他可能產生異常的代碼
        .......
        
       Toast.success("創(chuàng)建訂單成功");
    }

注意四個參數:

  • message: 出現錯誤時,打印的錯誤
  • toast: 出現錯誤,是否Toast
  • report: 出現錯誤,是否上報
  • log: 使用使用console.error打印

可能你說,這這,消息定死,不合理啊。我要是有其他消息呢。
此時我微微一笑別急, 再看一段代碼

  @methodCatch({ message: "創(chuàng)建訂單失敗", toast: true, report:true, log:true })
    async createOrder() {
        const data = {...};
        const res = await createOrder();
        if (!res || res.errCode !== 0) {
            return Toast.error("創(chuàng)建訂單失敗");
        }
       
        .......
        其他可能產生異常的代碼
        .......
        
       throw new CatchError("創(chuàng)建訂單失敗了,請聯系管理員", {
           toast: true,
           report: true,
           log: false
       })
       
       Toast.success("創(chuàng)建訂單成功");

    }

是都,沒錯,你可以通過拋出 自定義的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__";
    /**
     * 捕捉到的錯誤
     * @param message 消息
     * @options 其他參數
     */
    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: "創(chuàng)建訂單失敗", toast: true, report:true, log:true })
    async createOrder() {
        const data = {...};
        const res = await createOrder();
        if (!res || res.errCode !== 0) {
            return Toast.error("創(chuàng)建訂單失敗");
        }
       Toast.success("創(chuàng)建訂單成功");
       
        .......
        其他可能產生異常的代碼
        .......
        
       throw new CatchError("創(chuàng)建訂單失敗了,請聯系管理員", {
           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如何優(yōu)雅的捕獲異常的文章就介紹到這了,更多相關React 捕獲異常內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • React實現一個倒計時hook組件實戰(zhàn)示例

    React實現一個倒計時hook組件實戰(zhàn)示例

    這篇文章主要為大家介紹了React實現一個倒計時hook組件,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-02-02
  • react 中父組件與子組件雙向綁定問題

    react 中父組件與子組件雙向綁定問題

    這篇文章主要介紹了react 中父組件與子組件雙向綁定問題,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-05-05
  • React-RouterV6+AntdV4實現Menu菜單路由跳轉的方法

    React-RouterV6+AntdV4實現Menu菜單路由跳轉的方法

    這篇文章主要介紹了React-RouterV6+AntdV4實現Menu菜單路由跳轉,主要有兩種跳轉方式一種是編程式跳轉另一種是NavLink鏈接式跳轉,每種方式通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下
    2022-08-08
  • React中style的使用及注意事項(推薦)

    React中style的使用及注意事項(推薦)

    React中style的使用和直接在HTML中使用有些不同,第一,React中必須是style="opacity:{this.state.opacity};"這種寫法,第二如果設置多個style格式如下,多個style中間使用逗號分割,這篇文章主要介紹了React中style的使用注意事項,需要的朋友可以參考下
    2023-02-02
  • react中實現拖拽排序react-dnd功能

    react中實現拖拽排序react-dnd功能

    這篇文章主要介紹了react中實現拖拽排序react-dnd功能,本文結合實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-02-02
  • react-router4 嵌套路由的使用方法

    react-router4 嵌套路由的使用方法

    本篇文章主要介紹了react-router4 嵌套路由的使用方法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • React?時間切片理解分析

    React?時間切片理解分析

    這篇文章主要為大家介紹了React?時間切片理解分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-03-03
  • 通過示例源碼解讀React首次渲染流程

    通過示例源碼解讀React首次渲染流程

    這篇文章主要為大家通過示例源碼解讀React的首次渲染流程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-03-03
  • 深入React?18源碼useMemo?useCallback?memo用法及區(qū)別分析

    深入React?18源碼useMemo?useCallback?memo用法及區(qū)別分析

    這篇文章主要為大家介紹了React?18源碼深入分析useMemo?useCallback?memo用法及區(qū)別,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-04-04
  • React狀態(tài)提升案例介紹

    React狀態(tài)提升案例介紹

    這篇文章主要介紹了React狀態(tài)提升案例,所謂 狀態(tài)提升 就是將各個子組件的 公共state 提升到它們的父組件進行統(tǒng)一存儲、處理,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧
    2023-04-04

最新評論

长武县| 德昌县| 彭阳县| 江阴市| 新绛县| 双柏县| 长宁县| 阿坝县| 南宫市| 齐齐哈尔市| 河津市| 黄石市| 绥滨县| 达孜县| 新宾| 淮安市| 陵水| 石首市| 昭通市| 呼伦贝尔市| 商都县| 三都| 江孜县| 正宁县| 峨山| 伊金霍洛旗| 抚州市| 溧阳市| 探索| 泸西县| 揭西县| 平乐县| 阳春市| 墨脱县| 海城市| 保亭| 东海县| 盐边县| 岳阳县| 塘沽区| 上杭县|