react取消捕获_React中如何优雅的捕捉事件错误-阿里云开发者社区

React中如何优雅的捕捉事件错误

前话

人无完人,所以代码总会出错,出错并不可怕,关键是怎么处理。

我就想问问大家react的错误怎么捕捉呢? 这个时候:

小白:怎么处理?

小白+: ErrorBoundary

小白++: ErrorBoundary, try catch

小白#: ErrorBoundary, try catch, window.onerror

小白##: 这个是个严肃的问题,我知道*种处理方式,你有什么好的方案?

正题

小白#回答的基本就是解决思路。我们来一个一个简单说说。

1. EerrorBoundary

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

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

Error boundaries在rendering,lifeCyclemethod或处于他们树层级之下的构造函数中捕获错误

哦,原来如此。 怎么用

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

Something went wrong.

;

}

return this.props.children;

}

}

重点:error boundaries并不会捕捉这些错误:

事件处理器

异步代码

服务端的渲染代码

在error boundaries区域内的错误

2. try catch

简单有效的捕捉

handleClick = () => {

try {

// Do something that could throw

} catch (error) {

this.setState({ error });

}

}

3. window.onerror

超级奥特曼,只是错误信息比较不好分析。

4.其他

http请求

封装后,专门处理

状态管理redux,mobx等

封装拦截 ,我们在项目的应用mobx-state-tree基本都是在model里面拦截的

其他

自己看着办啊,都找我,我很忙的。

问题

啊?这么多事件处理和方法都要加try catch啊。 你笨啊window.onerror啊。

onerror是非常好,但是有个问题,错误细节不好分析,有大神说,正则解析。

我不扶墙扶你。

解决

decorator特性,装饰器。 create-react-app创建的app默认是不知此的装饰器的。

不要和我争,github地址上人家写着呢can-i-use-decorators?

那问题又来了,如何支持装饰器。

场景一:自己构建的项目

那还不简单的飞起

const {injectBabelPlugin} = require('react-app-rewired');

/* config-overrides.js */

module.exports = {

webpack: function override(config, env) {

// babel 7

config = injectBabelPlugin('transform-decorators-legacy',config)

// babel 6

config = injectBabelPlugin('transform-decorators',config)

return config;

}

}

关于装饰器这里不做过多的说明,修改类的行为。

这里又有几个点

装饰方法 装饰类 装饰getter, setter都可以,我们选在装饰方法和类

装饰类,如何排除系统内置方法和继承的方法

装饰的时候有参和无参数怎么处理

我们先写一个来检查内置方法的方法, 不够自己补全

const PREFIX = ['component', 'unsafe_']

const BUILTIN_METHODS = [

'constructor',

'render',

'replaceState',

'setState',

'isMounted',

'replaceState'

]

// 检查是不是内置方法

function isBuiltinMethods(name) {

if (typeof name !== 'string' || name.trim() === '') {

return false

}

// 以component或者unsafe_开头

if (PREFIX.some(prefix => name.startsWith(prefix)))) {

return true

}

// 其他内置方法

if (BUILTIN_METHODS.includes(name)) {

return true

}

return false

}

再弄一个装饰方法的方法, 这个方法参考了autobind.js

handleError是自己的错误处理函数,这里没有写出来

// 监听方法

function createDefaultSetter(key) {

return function set(newValue) {

Object.defineProperty(this, key, {

configurable: true,

writable: true,

// IS enumerable when reassigned by the outside word

enumerable: true,

value: newValue

});

return newValue;

};

}

function observerHandler(fn, callback) {

return (...args) => {

try {

fn(...args)

} catch (err) {

callback(err)

}

}

}

//方法的装饰器, params是额外的参数

function catchMethod(target, key, descriptor, ...params) {

if (typeof descriptor.value !== 'function') {

return descriptor

}

const { configurable, enumerable, value: fn } = descriptor

return {

configurable,

enumerable,

get() {

// 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 fn;

}

// 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 fn;

}

const boundFn = observerHandler(fn.bind(this), err => {

handleError(err, target, key, ...params)

})

defineProperty(this, key, {

configurable: true,

writable: true,

// NOT enumerable when it's a bound method

enumerable: false,

value: boundFn

});

boundFn.bound = true

return boundFn;

},

set: createDefaultSetter(key)

};

}

再来一个装饰类的

/**

* 检查是不是需要代理

* @param {*} method

* @param {*} descriptor

*/

function shouldProxy(method, descriptor) {

return typeof descriptor.value === 'function'

&& !isBuiltinMethods(method)

&& descriptor.configurable

&& descriptor.writable

&& !descriptor.value.bound

}

function catchClass(targetArg, ...params) {

// 获得所有自定义方法,未处理Symbols

const target = targetArg.prototype || targetArg

let descriptors = getOwnPropertyDescriptors(target)

for (let [method, descriptor] of Object.entries(descriptors)) {

if (shouldProxy(method, descriptor)) {

defineProperty(target, method, catchMethod(target, method, descriptors[method], ...params))

}

}

}

最后暴露一个自动识别方法和类的方法

/**

*

* 未拦截getter和setter

* 未拦截Symbols属性

*/

export default function catchError(...args) {

const lastArg = args[args.length - 1]

// 无参数方法

if (isDescriptor(lastArg)) {

return catchMethod(...args)

} else {

// 无参数class?? 需要改进

if (args.length === 1 && typeof args[0] !== 'string') {

return catchClass(...args)

}

// 有参

return (...argsList) => {

// 有参数方法

if (isDescriptor(argsList[argsList.length - 1])) {

return catchMethod(...[...argsList, ...args])

}

// 有参数class

return catchClass(...[...argsList, ...args])

}

}

}

基本成型。

怎么调用

装饰类

@catchError('HeHe')

class HeHe extends React.Component {

state = {

clicked: false

}

onClick(){

this.setState({

clicked:true

})

this.x.y.z.xxx

}

render(){

return (

)

}

}

装饰方法

class HeHe extends React.Component {

state = {

clicked: false

}

@catchError('HeHe onClick')

onClick(){

this.setState({

clicked:true

})

this.x.y.z.xxx

}

render(){

return (

)

}

}

当然你还可以既装饰类又装饰方法, 这个时候方法的装饰优先于类的装饰,不会重复装饰

@catchError('HeHe')

class HeHe extends React.Component {

state = {

clicked: false

}

@catchError('HeHe onClick')

onClick(){

this.setState({

clicked:true

})

this.x.y.z.xxx

}

onClick2(){

}

render(){

return (

)

}

}

如上,细心的人可以发现, 没有 onClick.bind(this), 是的, catchError会自动完成bind,是不是很cool。

如上,现在的所有的事件处理都会被catchError里面定义的handleError处理,怎么处理就看你自己了。

有人就问了,我要是想捕捉后还要有额外处理的了,比如来个提示框之类的。

这个就取决你的需求和怎么处理,你依旧可以在你的事件处理器try catch。

二是,你没看到@catchError里面可以传递参数么,可以提供额外的错误信息,比如场景,是不是致命错误等等信息。

她解决了你未显示处理的事件处理错误,有没有很优雅,有没有。

你们都说没有的话, 我就放弃前端了,可是我还有老婆孩子要养,所以你们一定要有人说有。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值