React 页面空白之错误边界
theme: juejin
highlight: a11y-dark
1、背景
某个不懂前端的领导问,为什么页面里随便报个错误就空白啊,你们到底是怎么处理的,封装的东西就有点问题啊,,,,劈里啪啦的说一大堆
2、思考
enen,回头想了想,从页面使用层面上来讲好像这个道理,网页的某个地方发生错误应该只影响到局部而不是整体效果。查阅官方文档后得知自react16之后,任何未被错误边界捕获的错误将会导致整个 React 组件树被卸载。 整个组件树被卸载,即页面出现空白,整个应用组件树都被卸载掉,空白那是肯定的。但既然这样做肯定还有补救的方法。
部分 UI 的 JavaScript 错误不应该导致整个应用崩溃,为了解决这个问题,React 16 引入了一个新的概念 —— 错误边界
4、解决
React中提供了两个与错误处理相关的api:
-
getderivedstatefromerror
:静态方法,当错误发生后,提供一个机会渲染 Fallback UI -
componentDidCatch
:组件实例方法,当错误发生后,提供一个机会记录错误信息
如果一个 class 组件中定义了 getDerivedStateFromError()
或 componentDidCatch()
这两个生命周期方法中的任意一个(或两个)时,那么它就变成一个错误边界。当抛出错误后,请使用 getDerivedStateFromError()
渲染备用 UI ,使用 componentDidCatch()
打印错误信息。
4-1、创建边界错误组件ErrorBoundary
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
// 错误状态
hasError: false,
};
}
getDerivedStateFromError(error) {
// 更新 state 使下一次渲染能够显示降级后的 UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 你同样可以将错误日志上报给服务器
logErrorToService(error, errorInfo);
}
render() {
if (this.state.hasError) {
// 你可以自定义降级后的 UI 并渲染
return <h1>出现错误啦,请解决</h1>;
}
return this.props.children;
}
}
export default ErrorBoundary;
4-2、在其它组件,路由里页面里引用包裹在最外层即可
<ErrorBoundary>
<div>你好</div>
</ErrorBoundary>
4-3、注意
错误边界的工作方式类似于原生的 catch {}
,不同的地方在于,错误边界只针对 React 组件。并且只有 class 组件才可以成为错误边界组件。大多数情况下, 你只需要声明一次错误边界组件, 并在整个应用中使用它
错误边界的粒度由你来决定,可以将其包装在最顶层的路由组件中,也可以根据需要放在某些组件上包裹,这个具体问题具体分析吧
4-4、适用情况
-
错误边界目前只在
Class Component
中实现了,没有在hooks
中实现(因为Error Boundaries的实现借助了this.setState可以传递callback的特性,useState无法传入回调,所以无法完全对标); -
错误边界 无法捕获 以下四种场景中产生的错误:
- 事件处理函数(因为 Error Boundaries 实现的本质是触发更新,但是事件处理函数不在render或者commit阶段,所以无法进行捕获,如果你需要在事件处理器内部捕获错误,可以使用原生的
try
/catch
语句 了解更多) - 异步代码(例如
setTimeout
或requestAnimationFrame
回调函数) - 服务端渲染(因为触发更新只能在客户端进行,不能在serve端进行)
- 它自身抛出来的错误(因为错误抛出要向父节点冒泡寻找 Error Boundaries 处理,无法处理自身产生的错误)
- 事件处理函数(因为 Error Boundaries 实现的本质是触发更新,但是事件处理函数不在render或者commit阶段,所以无法进行捕获,如果你需要在事件处理器内部捕获错误,可以使用原生的
5、示例
5-1、创建边界组件 ErrorBoundary
import React from 'react';
class ErrorBoundary extends React.Component {
static getDerivedStateFromError() {
return { hasError: true };
}
// 我这里处理的是每次出现错误给出提示
componentDidCatch() {
alert('应用出现错误');
}
render() {
// 每次出现错误都进行组件刷新
return this.props.children;
}
}
export default ErrorBoundary;
5-2、路由页面最外层嵌套使用
import React from 'react';
import ErrorBoundary from '../component/ErrorBoundary';
const index = (props:any) => {
return (
<ErrorBoundary>
<div>{ props.children }</div>
</ErrorBoundary>
);
}
export default index;
5-3、创建一个组件 ComDemo,里面实现个异常错误,通常这种错误会导致页面空白,整个应用崩溃
import React, { useState } from 'react';
import { Button, Input } from 'antd';
const index = ()=>{
const [isShow, setIsShow] = useState<any>([1,2,3]);
const fns = ()=>{
setIsShow(null);
}
return (
<div>
<Input></Input>
<div></div>
{
isShow.map((item:any) => {
return <div>{ item }</div>
})
}
我是子组件
<Button onClick={ fns }>点击啊</Button>
</div>
)
}
export default index;
5-4、页面里使用错误组件
import React, { useState } from 'react';
import ComDemo from '../../component/ComDemo';
class MyComponent extends React.Component {
render() {
return (
<div>
<ComDemo></ComDemo>
</div>
)
}
}
export default MyComponent;
跟着上面走下来就是一个完整使用过程了
问题
当捕获到边界异常的时候,刷新组件会导致整个页面刷新,在这种情况下,体验感还是不是很好,要怎么做到只影响出现异常的地方,其它地方不被影响,并保持之前的状态,这块我需要研究一下