-
Notifications
You must be signed in to change notification settings - Fork 25
Description
前言
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
andgetDerivedStateFromError
: There are no Hook equivalents for these methods yet, but they will be added soon.
总结
Error Boundaries
可以捕获所有子元素渲染时异常,包括 render
、各生命周期函数,但也有很多使用限制,我们需要正确使用它。
Error Boundaries
也不是万能的,更多时候我们要避免并及时修复错误以及错误兜底降低影响,并在第一时间内监控起来并快速修复。