部分 UI 的异常不应该破坏了整个应用。为了解决 React 用户的这一问题,React 16 引入了一种称为 “错误边界” 的新概念。错误边界是用于捕获其子组件树 JavaScript 异常,记录错误并展示一个回退的 UI 的 React 组件,而不是整个组件树的异常。错误组件在渲染期间,生命周期方法内,以及整个组件树构造函数内捕获错误。
错误边界通过componentDidCatch() 方法机来捕获异常,制类似于 JavaScript catch {},但记住,仅有类组件可以成为错误边界。如果一个错误边界无法渲染错误信息,则错误会向上冒泡至最接近的错误边界。这也类似于 JavaScript 中 catch {} 的工作机制。下面是一个错误边界的例子:
定义一个错误边界捕获的类ErrorBoundary.jsx:
import React from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: ""
};
}
componentDidCatch(error, info) {
this.setState({
hasError: true,
error: error
});
console.log("ErrorBoundary!!!");
}
render() {
if(this.state.hasError) {
return <h1>{this.state.error.toString()}</h1>;
}
return this.props.children;
}
}
//导出组件
export default ErrorBoundary;
触发错误边界的组件ErrorDemo.jsx:
import React from 'react';
class ErrorDemo extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
throw new Error('I crashed!');
return(
<div>No Error</div>
);
}
}
export default ErrorDemo;
渲染组件及错误边界用法:
import React from 'react';
import ErrorDemo from './components/errorboundary/ErrorDemo.jsx';
import ErrorBoundary from './components/errorboundary/ErrorBoundary.jsx';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return(
<ErrorBoundary>
<ErrorDemo />
</ErrorBoundary>
);
}
}
export default App;
此时发现在生产环境下,虽然会打印错误,但不影响程序继续正常运行,但如果在开发环境,页面依然会报错。
在最新的官方代码中,新增了静态方法getDerivedStateFromError用来改变ErrorBoundary.js的hasError状态,而方法componentDidCatch只用于打印错误日志,新的ErrorBoundary.js写法如下:
import React from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: ''
};
}
static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染能够显示降级后的 UI
return { hasError: true, error: error };
}
componentDidCatch(error, info) {
// 打印错误日志
console.log('ErrorBoundary:', error, info);
}
render() {
if (this.state.hasError) {
return <h1>{this.state.error.toString()}</h1>;
} else {
return this.props.children;
}
}
}
//导出组件
export default ErrorBoundary;