在已经发布的React 16 第一个beta版和即将发布的React 16中,添加了一个新的特性,用来处理React组件内的异常。
这个新特性是React提出的一个新的概念,叫“异常边界(Error Boundary)”。
如果你有兴趣,可以在React 16的beta版中体验一下。
阅读全文约4分钟。
本文同步发布与知乎专栏:前端微志。
什么是异常边界
在React 15和更早的版本中,组件内的JavaScript异常会污染React组件内的state,并且会在render时发出不明所以的异常。这些异常一般是由于系统运行过程中更多出现的异常,但是React没有提供一个组件内优雅地解决它们的方法,也不能恢复这些异常。
在开发者看来,用React做的一个大型系统上,UI页面上一个JavaScript代码的小小的异常,不应该影响整个系统的运行,这本来是浏览器在渲染JavaScript时默认采用的一种解析方式,但一般用React来做一个单页面应用(SPA)会把很多页面的代码打包到一起,如果一个JavaScript的报错导致整个系统不能运行,也是一件很糟糕的事情。
所以,这也算是React的设计上的一个小的缺陷,React为了帮助开发者解决这个问题,在React 16这一版中,将引入一个新概念“异常边界”,使用异常边界的组件就是异常边界组件。
异常边界是一个用来捕获组件的子组件树内的JavaScript异常,将这些异常记录下来,并显示一个回调的UI来替换掉崩溃的组件树。
异常边界组件会在组件render过程中,生命周期函数内和组件下的整个树形组件的构造函数内运行。
怎么使用异常边界
为了实现异常边界,React为组件提供了一个新的生命周期函数:
componentDidCatch(error, info)。
请看下面的栗子,我们来包装一个异常边界组件:
class ErrorBoundary extends React.Compoent {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
// 显示回调UI
this.setState({ hasError: true });
// 你也可以将异常记录到一个异常报警服务上
logErrorToMyServer(error, info);
}
render() {
if (this.state.hasError) {
// 你可以render任何自定义的回调UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
然后,你就可以将异常边界组件当做一个公用组件使用,如用它来包裹其他子组件,用于捕获子组件内的JavaScript异常。举个🌰:
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
异常边界的特性
通过上面的例子,你可能会觉得,componentDidCatch() 特别像JavaScript中的 catch() 方法块,只不过后者是用于JavaScript语句中的异常捕获,前者是用于React组件UI渲染过程中的异常捕获。
在使用异常边界时,需要注意几点:
-
只有类组件(class components)可以被设置成异常边界组件
-
通常情况下,你可能只声明一次异常边界组件,然后在整个项目中使用,当然你也可以设置多个
-
异常边界组件只能捕获它的子组件树上的组件的异常,且不能捕获该组件自身产生的异常
-
如果异常边界组件在捕获到一个异常后,渲染异常信息失败了,那么这个异常会向着该异常边界组件的父级向上传递,直到传递到最近的一个异常边界组件为止
在哪里设置异常边界组件
在项目中设置异常边界组件的颗粒度取决于开发者自己。
你可以像服务端渲染框架一样,在页面根路由组件上设置异常边界边界向用户展示“Something went wrong”这样的信息。
你也可以将单个组件用一个异常边界组件包裹起来,以防止这些组件崩溃时导致系统挂掉。
参考
https://facebook.github.io/react/blog/2017/07/26/error-handling-in-react-16.html
