2023-03-13 10:40:08 +08:00

178 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 7. 错误边界
这是从 *React 16* 引入了一个新的概念,为了解决的问题就是一个组件出错不至于整个应用崩溃。
首先我们还是来看一个示例:
```js
import React from 'react';
import ChildCom1 from "./components/ChildCom1"
import ChildCom2 from "./components/ChildCom2"
function App() {
return (
<div>
<ChildCom1/>
<ChildCom2/>
</div>
);
}
export default App;
```
```js
import ChildCom3 from "./ChildCom3";
function ChildCom1() {
return (
<div style={{
width: "300px",
height: "300px",
border: "1px solid"
}}>
ChildCom1
<ChildCom3 />
</div>
);
}
export default ChildCom1;
```
```js
function getData() {
return;
}
function ChildCom3() {
const arr = getData();
const spanContent = arr.map(it => <span>{it}</span>)
return (
<div style={{
width: "100px",
height: "100px",
border: "1px solid"
}}>
ChildCom3
<div>{spanContent}</div>
</div>
);
}
export default ChildCom3;
```
```js
function ChildCom2() {
return (
<div style={{
width: "300px",
height: "300px",
border: "1px solid"
}}>ChildCom2</div>
);
}
export default ChildCom2;
```
在上面的代码中,我们整个组件树结构如下:
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-12-08-065452.png" alt="image-20221208145452107" style="zoom:50%;" />
可以看到,*ChildCom1* 下面的 *ChildCom3* 存在问题,这一个组件的问题会导致整个应用崩溃。
**<u>这在某些场景下,实际上是没有必要的,例如有问题的组件是广告、或者一些无关紧要的组件,此时我们就期望渲染出问题组件以外的组件树。</u>**
错误边界就是用来解决这个问题的。
>错误边界是一种 *React* 组件,这种组件可以捕获发生在其子组件树任何位置的 *JavaScript* 错误,并打印这些错误,同时展示降级 *UI*,而并不会渲染那些发生崩溃的子组件树。错误边界可以捕获发生在整个子组件树的渲染期间、生命周期方法以及构造函数中的错误。
```js
// components/ErrorBoundary.jsx
import React, { Component } from 'react'
export default class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染能够显示降级后的 UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 你同样可以将错误日志上报给服务器
console.log("error>>>", error)
console.log("errorInfo>>>", errorInfo)
}
render() {
if (this.state.hasError) {
// 你可以自定义降级后的 UI 并渲染
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
```
在上面的代码中,我们就创建了一个错误边界组件,该组件有一个 *getDerivedStateFromError* 静态方法以及 *componentDidCatch* 实例方法,这两个方法都会在组件渲染出错时调用,但是略有区别,具体的区别如下:
- *getDerivedStateFromError* 静态方法
- 运行时间点:渲染子组件的过程中,发生错误之后,在更新页面之前(整个应用没有崩溃,直接渲染降级 UI
- **注意:只有子组件发生错误,才会运行该函数**
- 该函数返回一个对象,*React* 会将该对象的属性覆盖掉当前组件的 *state*
- 参数:错误对象
- 通常,**<u>该函数用于改变状态</u>**
- *componentDidCatch* 实例方法
- 运行时间点渲染子组件的过程中发生错误更新页面之后整个应用已经崩溃了之后再重新渲染整个应用当然会排除有问题的那一部分UI那一部分渲染降级UI由于其运行时间点比较靠后**<u>因此不太会在该函数中改变状态</u>**
- 通常,**<u>该函数用于记录错误消息</u>**
>最佳实践,使用 *static getDerivedStateFromError* 渲染备用 *UI* ,使用 *componentDidCatch* 打印错误信息。
之后,我们就使用这个错误边界组件来包裹要忽略渲染错误的子组件,例如:
```js
import ChildCom3 from "./ChildCom3";
import ErrorBoundary from "./ErrorBoundary"
function ChildCom1() {
return (
<div style={{
width: "300px",
height: "300px",
border: "1px solid"
}}>
ChildCom1
<ErrorBoundary>
<ChildCom3 />
</ErrorBoundary>
</div>
);
}
export default ChildCom1;
```
有了错误边界组件后,*ChildCom3* 组件中的渲染错误并不会导致整个应用崩溃。效果如下:
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-12-08-065524.png" alt="image-20221208145524497" style="zoom:67%;" />
最后需要注意的是,错误边界组件主要是用来捕获 *UI* 渲染时的错误,因此如下场景中错误是无法捕获的:
- 事件处理
- 异步代码
- 服务端渲染
- 它自身抛出来的错误
总之,错误边界组件仅能过处理**渲染子组件期间**的**同步错误**。
---
-*EOF*-