React 错误边界 Error Boundaries 捕获渲染时的错误
什么是 React 错误边界
错误边界是一种 React 组件,它能够捕获渲染过程、生命周期方法以及构造函数中发生的 JavaScript 错误,并展示回退 UI(Fallback UI),从而避免整个应用白屏崩溃。
错误边界无法捕获以下场景的错误:
- 事件处理函数中的错误(可以使用
try...catch) - 异步代码(例如
setTimeout、Promise) - 服务端渲染(SSR)期间
- 错误边界自身抛出的错误
错误边界的工作方式类似于 JavaScript 的 catch {},但只针对组件树。
为什么需要错误边界
React 16 之前,组件内的错误会导致整个组件树被卸载,用户看到的是一个破碎的白屏。错误边界提供了一种“优雅降级”的机制,将错误隔离在局部,保证剩余部分仍可正常交互。
使用错误边界可以:
- 防止整个页面崩溃
- 向用户展示友好的错误提示
- 记录错误日志,便于排查问题
- 提供重试或恢复操作
如何创建错误边界
一个类组件只要实现 static getDerivedStateFromError() 或 componentDidCatch()(或两者同时实现),它就成为了一个错误边界。
getDerivedStateFromError
- 静态方法,返回一个对象用于更新 state
- 允许您在错误发生后渲染降级 UI
- 在“渲染”阶段调用,因此不应包含副作用
static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染显示降级 UI
return { hasError: true };
}
componentDidCatch
- 实例方法,用于记录错误信息
- 接收
error(抛出的错误)和errorInfo(包含组件调用栈的对象) - 在“提交”阶段调用,可以执行副作用(如日志上报)
componentDidCatch(error, errorInfo) {
// 可以在这里将错误报告给第三方服务
console.error(error, errorInfo);
}
完整示例
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 可添加错误日志服务
logErrorToService(error, errorInfo);
}
render() {
if (this.state.hasError) {
// 自定义降级 UI
return <h1>页面出了点问题,请刷新重试。</h1>;
}
return this.props.children;
}
}
使用错误边界
将错误边界包裹在可能出错的子组件外层即可。可以包裹顶层路由,也可以包裹粒度更小的组件,以便隔离影响范围。
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
推荐在以下位置设置错误边界:
- 顶层路由组件:防止导航切换时的渲染错误导致整个应用崩溃。
- 独立的微组件或第三方组件库:无法保证其内部永远不抛错。
- 懒加载组件:与 React.lazy 结合使用时,可以同时处理加载和错误状态。
包裹路由示例
function App() {
return (
<ErrorBoundary fallback={<p>当前页面不可用</p>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/profile" element={<Profile />} />
</Routes>
</ErrorBoundary>
);
}
嵌套使用
错误边界可以嵌套,错误会被最近的父级错误边界捕获。
<ErrorBoundary fallback={<DashboardError />}>
<Dashboard>
<ErrorBoundary fallback={<WidgetError />}>
<ComplexWidget />
</ErrorBoundary>
</Dashboard>
</ErrorBoundary>
错误边界与功能组件
React 官方目前没有提供 Hooks 版本的错误边界。错误边界必须使用类组件来声明。
对于函数组件,您可以:
- 使用现有的第三方库(如
react-error-boundary)获得声明式 API - 自行封装一个类组件错误边界,然后在函数组件树中使用
使用 react-error-boundary(推荐)
安装:
npm install react-error-boundary
使用:
import { ErrorBoundary } from 'react-error-boundary';
function FallbackComponent({ error, resetErrorBoundary }) {
return (
<div role="alert">
<p>发生错误:{error.message}</p>
<button onClick={resetErrorBoundary}>重试</button>
</div>
);
}
<ErrorBoundary
FallbackComponent={FallbackComponent}
onReset={() => {
// 重置应用状态
}}
>
<MyComponent />
</ErrorBoundary>
错误恢复策略
一个好的降级 UI 通常包含“重试”或“重置”机制。可以通过 key 属性强制重新挂载错误边界内部的组件。
手动实现重置
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
handleRetry = () => {
this.setState({ hasError: false });
};
render() {
if (this.state.hasError) {
return (
<div>
<p>加载失败</p>
<button onClick={this.handleRetry}>点击重试</button>
</div>
);
}
return this.props.children;
}
}
另一种方式是利用 key 变化让 React 重新创建组件:
<ErrorBoundary key={someKey}>
<MyComponent />
</ErrorBoundary>
当 someKey 改变时,ErrorBoundary 和其子节点都会被全新挂载。
错误边界的最佳实践
-
粒度适中
不必为每个组件都套上错误边界,通常在页面、区块、微前端子应用等级别设置即可。 -
结合日志系统
在componentDidCatch或第三方库的onError回调中上报错误信息和组件栈,以便快速定位生产问题。 -
提供有意义的回退 UI
根据上下文给出不同的降级界面,不应只是一个空白页或通用错误提示。 -
不要忽略错误边界的自身错误
如果错误边界自身抛出错误,该错误会冒泡到上层的错误边界。确保顶层至少有一个根错误边界。 -
配合 Suspense 处理异步组件
React.lazy加载失败时,如果包裹了错误边界,可以捕获ChunkLoadError等加载错误。
注意事项
- 错误边界无法捕获事件处理器错误,请务必在事件处理内部使用
try...catch。 componentDidCatch中产生的错误不会再次被该边界捕获,将被传递给上层边界。- 开发环境下,错误边界显示的 UI 会在报错后快速消失,以便开发者看到错误堆栈;生产环境会稳定展示降级 UI。
总结
错误边界是 React 应用中保障稳定性的核心机制之一。通过合理设计错误边界层次结构,并结合降级 UI 与日志上报,您可以大幅提升应用的健壮性和用户体验。对于函数组件,推荐使用 react-error-boundary 简化开发,同时支持更灵活的重置与恢复模式。