1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        React捕獲異常, 含Hooks方案

        共 11051字,需瀏覽 23分鐘

         ·

        2022-03-18 10:37

        本文適合對React捕獲異常,以及對hook封裝感興趣的小伙伴閱讀。

        歡迎關(guān)注前端早茶,與廣東靚仔攜手共同進階~

        一、前言

        在React項目中,因為事件處理程序總是需要寫?try/catch,不勝其煩。
        雖然可以丟給window.onerror或者?window.addEventListener("error")去處理,但是對錯誤細節(jié)的捕獲以及錯誤的補償是極其不友好的。

        二、一覽風姿

        我們捕獲的范圍:
        1. Class的靜態(tài)同步方法
        2. Class的靜態(tài)異步方法
        3. Class的同步方法
        4. Class的異步方法
        5. Class的同步屬性賦值方法
        6. Class的異步屬性賦值方法
        7. Class的getter方法
        8. Hooks方法
        getter這里是不是很類似 vue的 計算值,所以以后別說我大React沒有計算屬性,哈哈。
        來來來,一覽其風采:

        Class組件


        interface?State?{
        ????price:?number;
        ????count:?number;
        }

        export?default?class?ClassT?extends?BaseComponent<{},?State>?{
        ????constructor(props)?{
        ????????super(props);
        ????????this.state?=?{
        ????????????price:?100,
        ????????????count:?1
        ????????}
        ????????this.onIncrease?=?this.onIncrease.bind(this);
        ????????this.onDecrease?=?this.onDecrease.bind(this);
        ????}

        ????componentDidMount()?{
        ????????ClassT.printSomething();
        ????????ClassT.asyncPrintSomething();

        ????????this.doSomethings();
        ????????this.asyncDoSomethings();
        ????}

        ????@catchMethod({?message:?"printSomething?error",?toast:?true?})
        ????static?printSomething()?{
        ????????throw?new?CatchError("printSomething?error:?主動拋出");
        ????????console.log("printSomething:",?Date.now());
        ????}

        ????@catchMethod({?message:?"asyncPrintSomething?error",?toast:?true?})
        ????static?async?asyncPrintSomething()?{
        ????????const?{?run?}?=?delay(1000);
        ????????await?run();
        ????????throw?new?CatchError("asyncPrintSomething?error:?主動拋出");
        ????????console.log("asyncPrintSomething:",?Date.now());
        ????}

        ????@catchGetter({?message:?"計算價格失敗",?toast:?true?})
        ????get?totalPrice()?{
        ????????const?{?price,?count?}?=?this.state;
        ????????//?throw?new?Error("A");
        ????????return?price?*?count;
        ????}

        ????@catchMethod("增加數(shù)量失敗")
        ????async?onIncrease()?{

        ????????const?{?run?}?=?delay(1000);
        ????????await?run();

        ????????this.setState({
        ????????????count:?this.state.count?+?1
        ????????})
        ????}

        ????@catchMethod("減少數(shù)量失敗")
        ????onDecrease()?{
        ????????this.setState({
        ????????????count:?this.state.count?-?1
        ????????})
        ????}

        ????@catchInitializer({?message:?"catchInitializer?error",?toast:?true?})
        ????doSomethings?=?()?=>?{
        ????????console.log("do?some?things");
        ????????throw?new?CatchError("catchInitializer?error:?主動拋出");
        ????}

        ????@catchInitializer({?message:?"catchInitializer?async?error",?toast:?true?})
        ????asyncDoSomethings?=?async?()?=>?{
        ????????const?{?run?}?=?delay(1000);
        ????????await?run();
        ????????throw?new?CatchError("catchInitializer?async?error:?主動拋出");
        ????}

        ????render()?{
        ????????const?{?onIncrease,?onDecrease?}?=?this;
        ????????const?totalPrice?=?this.totalPrice;

        ????????return?????????????padding:?"150px",
        ????????????lineHeight:?"30px",
        ????????????fontSize:?"20px"
        ????????}}>
        ????????????
        價格:{this.state.price}</div>
        ????????????
        數(shù)量:1div>
        ????????????

        ????????????????增加數(shù)量</button>
        ????????????????減少數(shù)量button>
        ????????????</div>
        ????????????
        {totalPrice}div>
        ????????</div>
        ????}

        }
        再看看函數(shù)式組件,就是大家關(guān)注的Hooks,包裝出useCatch,底層是基于useMemo
        const?HooksTestView:?React.FC?=?function?(props)?{

        ????const?[count,?setCount]?=?useState(0);

        ????
        ????const?doSomething??=?useCatch(async?function(){
        ????????console.log("doSomething:?begin");
        ????????throw?new?CatchError("doSomething?error")
        ????????console.log("doSomething:?end");
        ????},?[],?{
        ????????toast:?true
        ????})

        ????const?onClick?=?useCatch(async?(ev)?=>?{
        ????????console.log(ev.target);
        ????????setCount(count?+?1);

        ????????doSomething();


        ????????const?d?=?delay(3000,?()?=>?{
        ????????????setCount(count?=>?count?+?1);
        ????????????console.log()
        ????????});
        ????????console.log("delay?begin:",?Date.now())
        ????????await?d.run();
        ????????console.log("delay?end:",?Date.now())
        ????????console.log("TestView",?this);
        ????????(d?as?any).xxx.xxx.x.x.x.x.x.x.x.x.x.x.x
        ????????//?throw?new?CatchError("自定義的異常,你知道不")
        ????},
        ????????[count],
        ????????{
        ????????????message:?"I?am?so?sorry",
        ????????????toast:?true
        ????????});

        ????return?

        ????????
        點我</button>div>
        ????????
        {count}</div>
        ????div>
        }

        export?default?React.memo(HooksTestView);

        三、優(yōu)化

        封裝getOptions方法

        //?options類型白名單
        const?W_TYPES?=?["string",?"object"];

        export?function?getOptions(options:?string?|?CatchOptions)?{
        ????const?type?=?typeof?options;
        ????let?opt:?CatchOptions;
        ????
        ????if?(options?==?null?||?!W_TYPES.includes(type))?{?//?null?或者?不是字符串或者對象
        ????????opt?=?DEFAULT_ERRPR_CATCH_OPTIONS;
        ????}?else?if?(typeof?options?===?"string")?{??//?字符串
        ????????opt?=?{
        ????????????...DEFAULT_ERRPR_CATCH_OPTIONS,
        ????????????message:?options?||?DEFAULT_ERRPR_CATCH_OPTIONS.message,
        ????????}
        ????}?else?{?//?有效的對象
        ????????opt?=?{?...DEFAULT_ERRPR_CATCH_OPTIONS,?...options?}
        ????}

        ????return?opt;
        }

        定義默認處理函數(shù)

        /**
        ?*?
        ?*?@param?err?默認的錯誤處理函數(shù)
        ?*?@param?options?
        ?*/

        function?defaultErrorHanlder(err:?any,?options:?CatchOptions)?{
        ????const?message?=?err.message?||?options.message;
        ????console.error("defaultErrorHanlder:",?message,?err);
        }

        區(qū)分同步方法和異步方法

        export?function?observerHandler(fn:?AnyFunction,?context:?any,?callback:?ErrorHandler)?{
        ????return?async?function?(...args:?any[])?{
        ????????try?{
        ????????????const?r?=?await?fn.call(context?||?this,?...args);
        ????????????return?r;
        ????????}?catch?(err)?{
        ????????????callback(err);
        ????????}
        ????};
        }

        export?function?observerSyncHandler(fn:?AnyFunction,?context:?any,?callback:?ErrorHandler)?{
        ????return?function?(...args:?any[])?{
        ????????try?{
        ????????????const?r?=?fn.call(context?||?this,?...args);
        ????????????return?r;
        ????????}?catch?(err)?{
        ????????????callback(err);
        ????????}
        ????};
        }

        具備多級選項定義能力

        export?default?function?createErrorCatch(handler:?ErrorHandlerWithOptions,?
        baseOptions:?CatchOptions?=?DEFAULT_ERRPR_CATCH_OPTIONS
        )?
        {

        ????return?{
        ????????catchMethod(options:?CatchOptions?|?string?=?DEFAULT_ERRPR_CATCH_OPTIONS)?{
        ????????????return?catchMethod({?...baseOptions,?...getOptions(options)?},?handler)
        ????????}???
        ????}
        }

        自定義錯誤處理函數(shù)

        export?function?commonErrorHandler(error:?any,?options:?CatchOptions)?{????
        ????try{
        ????????let?message:?string;
        ????????if?(error.__type__?==?"__CATCH_ERROR__")?{
        ????????????error?=?error?as?CatchError;
        ????????????const?mOpt?=?{?...options,?...(error.options?||?{})?};

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

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

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

        ????????}?else?{

        ????????????message?=?options.message?||??error.message;
        ????????????console.error("asyncMethodCatch:",?message,?error);

        ????????????if?(options.toast)?{
        ????????????????Toast.error(message);
        ????????????}
        ????????}
        ????}catch(err){
        ????????console.error("commonErrorHandler?error:",?err);
        ????}
        }


        const?errorCatchInstance?=?createErrorCatch(commonErrorHandler);

        export?const?catchMethod?=?errorCatchInstance.catchMethod;?

        四、增強

        支持getter

        看一下catchGetter的使用

        class?Test?{

        ????constructor(props)?{
        ????????super(props);
        ????????this.state?=?{
        ????????????price:?100,
        ????????????count:?1
        ????????}

        ????????this.onClick?=?this.onClick.bind(this);
        ????}

        ????@catchGetter({?message:?"計算價格失敗",?toast:?true?})
        ????get?totalPrice()?{
        ????????const?{?price,?count?}?=?this.state;
        ????????throw?new?Error("A");
        ????????return?price?*?count;
        ????}
        ????
        ??????render()?{???
        ????????const?totalPrice?=?this.totalPrice;

        ????????return?

        ????????????
        價格:{this.state.price}</div>
        ????????????
        數(shù)量:1div>
        ????????????
        {totalPrice}</div>
        ????????div>
        ????}
        ????
        }

        實現(xiàn)

        /**
        ?*?class?{??get?method(){}?}
        ?*?@param?options?
        ?*?@param?hanlder?
        ?*?@returns?
        ?*/

        export?function?catchGetter(options:?string?|?CatchOptions?=?DEFAULT_ERRPR_CATCH_OPTIONS,?
        hanlder:?ErrorHandlerWithOptions?=?defaultErrorHanlder
        )?
        {

        ????let?opt:?CatchOptions?=?getOptions(options);

        ????return?function?(_target:?any,?_name:?string,?descriptor:?PropertyDescriptor)?{
        ????????const?{?constructor?}?=?_target;
        ????????const?{?get:?oldFn?}?=?descriptor;

        ????????defineProperty(descriptor,?"get",?{
        ????????????value:?function?()?{
        ????????????????//?Class.prototype.key?lookup
        ????????????????//?Someone?accesses?the?property?directly?on?the?prototype?on?which?it?is
        ????????????????//?actually?defined?on,?i.e.?Class.prototype.hasOwnProperty(key)

        ????????????????if?(this?===?_target)?{
        ????????????????????return?oldFn();
        ????????????????}
        ????????????????//?Class.prototype.key?lookup
        ????????????????//?Someone?accesses?the?property?directly?on?a?prototype?but?it?was?found
        ????????????????//?up?the?chain,?not?defined?directly?on?it
        ????????????????//?i.e.?Class.prototype.hasOwnProperty(key)?==?false?&&?key?in?Class.prototype

        ????????????????if?(
        ????????????????????this.constructor?!==?constructor?&&
        ????????????????????getPrototypeOf(this).constructor?===?constructor
        ????????????????)?{
        ????????????????????return?oldFn();
        ????????????????}
        ????????????????const?boundFn?=?observerSyncHandler(oldFn,?this,?function?(error:?Error)?{
        ????????????????????hanlder(error,?opt)
        ????????????????}
        );
        ????????????????(boundFn?as?any)._bound?=?true;
        ????????????
        ????????????????return?boundFn();
        ????????????}
        ????????});

        ????????return?descriptor;
        ????}

        }

        支持屬性定義和賦值

        class?Test{
        ????@catchInitializer("nono")
        ????doSomethings?=?()=>?{
        ????????console.log("do?some?things");
        ????}
        }

        實現(xiàn)

        export?function?catchInitializer(options:?string?|?CatchOptions?=?DEFAULT_ERRPR_CATCH_OPTIONS,?hanlder:?ErrorHandlerWithOptions?=?defaultErrorHanlder){

        ????const?opt:?CatchOptions?=?getOptions(options);

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

        ????????console.log("debug....");
        ????????const?initValue?=?descriptor.initializer();
        ????????if?(typeof?initValue?!==?"function")?{
        ????????????return?descriptor;
        ????????}

        ????????descriptor.initializer?=?function()?{
        ????????????initValue.bound?=?true;
        ????????????return?observerSyncHandler(initValue,?this,?function?(error:?Error)?{
        ????????????????hanlder(error,?opt)
        ????????????});
        ????????};
        ????????return?descriptor;
        ????}
        }

        支持Hooks

        使用Demo:


        const?TestView:?React.FC?=?function?(props)?{

        ????const?[count,?setCount]?=?useState(0);

        ????
        ????const?doSomething??=?useCatch(async?function(){
        ????????console.log("doSomething:?begin");
        ????????throw?new?CatchError("doSomething?error")
        ????????console.log("doSomething:?end");
        ????},?[],?{
        ????????toast:?true
        ????})

        ????const?onClick?=?useCatch(async?(ev)?=>?{
        ????????console.log(ev.target);
        ????????setCount(count?+?1);

        ????????doSomething();

        ????????const?d?=?delay(3000,?()?=>?{
        ????????????setCount(count?=>?count?+?1);
        ????????????console.log()
        ????????});
        ????????console.log("delay?begin:",?Date.now())

        ????????await?d.run();
        ????????
        ????????console.log("delay?end:",?Date.now())
        ????????console.log("TestView",?this)
        ????????throw?new?CatchError("自定義的異常,你知道不")
        ????},
        ????????[count],
        ????????{
        ????????????message:?"I?am?so?sorry",
        ????????????toast:?true
        ????????});

        ????return?

        ????????
        點我</button>div>
        ????????
        {count}</div>
        ????div>
        }

        export?default?React.memo(TestView);

        實現(xiàn): 其基本原理就是利用?useMemo和之前封裝的observerHandler,寥寥幾行代碼就實現(xiàn)了。


        export?function?useCatch<T?extends?(...args:?any[])?=>?any>(callback:?T,?deps:?DependencyList,?options:?CatchOptions?=DEFAULT_ERRPR_CATCH_OPTIONS):?T?{????

        ????const?opt?=??useMemo(?()=>?getOptions(options),?[options]);
        ????
        ????const?fn?=?useMemo((..._args:?any[])?=>?{
        ????????const?proxy?=?observerHandler(callback,?undefined,?function?(error:?Error)?{
        ????????????commonErrorHandler(error,?opt)
        ????????});
        ????????return?proxy;

        ????},?[callback,?deps,?opt])?as?T;

        ????return?fn;
        }

        文章轉(zhuǎn)載于:云的世界

        聲明:文章著作權(quán)歸作者所有,如有侵權(quán),請聯(lián)系小編刪除。

        五、最后

        具備類似功能的庫:
        catch-decorator
        catch-decorator-ts
        catch-error-decorator

        關(guān)注我,一起攜手進階

        歡迎關(guān)注前端早茶,與廣東靚仔攜手共同進階~

        瀏覽 149
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            黑人久久 | 91人妻无码成人精品一区二区三区 | 自拍毛片在线观看 | .asian美白裸体女pics | 免费人成A片观看x8x8 | 五月丁香色播 | 国产成人精品无码免费看夜聊软件 | 欧美巨乳在线观看 | 中文字幕在线看片 | 无码国产精品一区二区 |