前言:做笔记,参考:react文档,文章涉及的示例:https://sanhuamao1.coding.net/public/note/myapp/git/files/13Error-Boundaries
错误边界
错误边界是一种 React 组件,可以捕获并打印发生在其子组件树任何位置的 JavaScript 错误,并且渲染出备用 UI。错误边界在渲染期间、生命周期和整个组件树的构造函数中捕获错误。
它无法在以下场景捕获错误:
- 事件处理(了解更多)
- 异步代码(例如 setTimeout 或 requestAnimationFrame 回调函数)
- 服务端渲染
- 它自身抛出来的错误(并非它的子组件)
使用方式
这是两种生命周期方法。只要class组件用了任意一种,那它就变成一个错误边界组件
componentDidCatch()
:打印错误static getDerivedStateFromError()
:渲染备用 UI
方式一:componentDidCatch()
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, errorInfo) {
this.setState({
error:error,
errorInfo:errorInfo
})
}
render() {
if (this.state.errorInfo) {
return <div>
{this.state.error&&this.state.error.toString()}<br/> {this.state.errorInfo.componentStack}
</div>
}
//正常则返回子元素,即该组件包裹的元素
return this.props.children;
}
}
一个测试组件,如果输入的是非字母就会报错:
class Example extends React.Component{
constructor(props){
super(props)
this.state={value:""}
this.handleChange=(e)=>{
this.setState({value:e.target.value})
}
}
render(){
const value=this.state.value
if(/^[a-zA-Z]+$/.test(value)||value===""){
return <input type="text" onChange={this.handleChange} placeholder="只能填字母"/>
}
throw new Error('出错了!');
}
}
App.js
<ErrorBoundary>
<Example />
</ErrorBoundary>
方式二:static getDerivedStateFromError()
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
render() {
if (this.state.hasError) {
//显示降级UI
return <div>出错了bro!</div>
}
return this.props.children;
}
}
你也可以同时使用两种。
看样子componentDidCatch()
好像没什么用,因为一般情况下浏览器会告诉你只能在控制台看错误日志,并且在没有使用该错误边界组件的情况下,控制台也会打印出同样的错误信息。相比之下,能渲染出备用组件的static getDerivedStateFromError()
的实用性高一点。其实,无论使用哪一种,它都起到了创建错误边界组件的作用。
错误边界组件的工作方式类似于 JavaScript 的
catch {}
,不同之处在于它只针对 React 组件。且只有 class 组件才可以成为错误边界组件。注意,它无法捕获其自身的错误。
如果没有使用错误边界会怎样?
自 React 16 起,任何未被错误边界捕获的错误将会导致整个 React 组件树被卸载。
经验告诉我们,完全移除比保留错误UI更好。例如,在类似 Messenger 的产品中,把异常的 UI 展示给用户可能会导致用户将信息错发给别人。
增加错误边界能够让你在应用发生异常时提供更好的用户体验。例如,Facebook Messenger 将侧边栏、信息面板、聊天记录以及信息输入框包装在单独的错误边界中。如果其中的某些 UI 组件崩溃,其余部分仍然能够交互。
try/catch的缺陷
try
/ catch
很棒但它仅能用于命令式代码:
try {
showButton();
} catch (error) {
// ...
}
然而,React 组件是声明式,并且具体指出 什么 需要被渲染:
<Button />
为什么无法捕获事件处理器的内部错误?
React 不需要错误边界来捕获事件处理器中的错误。与 render 方法和生命周期方法不同,事件处理器不会在渲染期间触发。因此,如果它们抛出异常,React 仍然能够知道需要在屏幕上显示什么。如果你需要在事件处理器内部捕获错误,使用普通的 JavaScript try
/ catch
语句:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { error: null };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
try {
// 执行操作,如有错误则会抛出
} catch (error) {
this.setState({ error });
}
}
render() {
if (this.state.error) {
return <h1>捕获到一个错误</h1>
}
return <button onClick={this.handleClick}>点击</button>
}
}