React 错误边界(Error Boundaries)
前言
react 项目中,很常见遇到页面由于某个 React 组件渲染错误(代码书写错误不规范或后端接口字段调整出错),导致整个应用被挂载出现白屏,且可能无法追踪造成影响极大,究其原因觉得是React 设计的坑点 自 React 16 起,任何未被错误边界捕获的错误将会导致整个 React 组件树被卸载。 针对此类问题,我们如何来进行感知上报以及应急兜底呢?
Error Boundaries 是 React16 提出来用来捕获渲染时错误的概念,Error Boundaries 是一种 React 组件,这种组件可以捕获并打印发生在其子组件树任何位置的 JavaScript 错误,且会渲染出兜底 UI。
Error Boundaries
Error Boundaries 可以用来捕获渲染时错误,api 如下:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染能够显示降级后的 UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 错误上报
logErrorToMyService(error, errorInfo);
}
render() {
if (this.state.hasError) {
// 自定义降级后的 UI 并渲染
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}- static getDerivedStateFromError:在出错后有机会修改 state 触发最后一次错误 fallback 的渲染。
- componentDidCatch:用于出错时副作用代码,比如错误上报等。
这两种方法中任意一个被定义时,这个组件就会成为 Error Boundaries 组件,可以阻止子组件渲染时报错。
错误边界的工作方式类似于 JavaScript 的 catch {},不同的地方在于错误边界只针对 React 组件。只有 class 组件才可以成为错误边界组件。
建议将 Error Boundary 单独作为一个组件,而不是将错误监听方法与业务组件耦合,一方面考虑到复用,另一方面则因错误检测只对子组件生效。
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>注意: 错误边界仅可以捕获其子组件的错误,无法捕获其自身的错误。如果一个错误边界无法渲染错误信息,则错误会冒泡至最近的上层错误边界,这也类似于 JavaScript 中 catch {} 的工作机制。
Error Boundaries 无法捕获错误
React Error Boundaries 官方文档 里提到了四种无法 Catch 的错误场景:
回调事件,由于回调事件执行时机不在渲染周期内,因此无法被 Error Boundaries Catch 住,如有必要得自行 try/catch
异步代码,比如 setTimeout 或 requestAnimationFrame,和第一条同理
服务端渲染
Error Boundaries 组件自身触发的错误,只能捕获其子组件的错误
这也是使用 Error Boundaries 最容易有疑问的地方。
对于不能捕获到的错误情况, 是因为 getDerivedStateFromError 执行在 render 阶段,componentDidCatch 执行在 commit 阶段,过了这两个阶段(即一次渲染周期)就无法捕获到。

无法捕获编译时错误
React 官方 API Error Boundaries 也只能捕获运行时错误,而对编译时错误无能为力。
编译时错误包括不限于编译环境错误、运行前的框架错误检查提示、TS/Flow 类型错误等,这些都是 Error Boundaries 无法捕获的,且没有更好的办法 Catch 住,遇到编译错误就在编译时解决吧,仅关注运行时错误就好了。
Error Boundaries 可作用于 Function Component
虽然函数式组件无法定义 Error Boundaries,但 Error Boundaries 可以捕获函数式组件的异常错误:
// ErrorBoundary 组件
class ErrorBoundary extends React.Component {
// ...
}
// Hooks 函数组件
const Child = (props) => {
React.useEffect(() => {
console.log(1);
props.a.b;
console.log(2);
}, [props.a.b]);
return <div />;
};
// 可以捕获所有组件异常,包括 Function Component 的子组件
const App = () => {
return (
<ErrorBoundary>
<Child />
</ErrorBoundary>
);
};
注意:出现在 deps 中的错误会立即被 Catch,导致 console.log(1) 都无法打印。但如果是下面的代码,则可以打印出 console.log(1),无法打印出 console.log(2):
const Child = (props) => {
React.useEffect(() => {
console.log(1);
props.a.b;
console.log(2);
}, []);
return <div />;
};
所以 React 官网的这句话并不是指 Error Boundaries 对 Hooks 不生效,而是指 Error Boundaries 无法以 Hooks 方式指定,对功能是没有影响的:
getSnapshotBeforeUpdate, componentDidCatch and getDerivedStateFromError: There are no Hook equivalents for these methods yet, but they will be added soon.
总结
Error Boundaries 可以捕获所有子元素渲染时异常,包括 render、各生命周期函数,但也有很多使用限制,我们需要正确使用它。
Error Boundaries 也不是万能的,更多时候我们要避免并及时修复错误以及错误兜底降低影响,并在第一时间内监控起来并快速修复。
来自:https://github.com/pfan123/Articles/issues/79
本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!