React 错误边界(Error Boundary)详解
ErrorBoundary(错误边界)是 React 提供的一种错误捕获机制,用于捕获其子组件树中的 JavaScript 运行时错误,并显示降级 UI,而不是让整个应用崩溃。
1. ErrorBoundary 能解决什么问题?
假设有个组件:
function UserInfo() {
throw new Error('获取用户信息失败');
return <div>用户信息</div>;
}
如果没有 ErrorBoundary:
<App>
<UserInfo />
</App>
页面会直接白屏:
Error: 获取用户信息失败
有 ErrorBoundary:
<ErrorBoundary>
<UserInfo />
</ErrorBoundary>
页面显示:
系统异常,请稍后重试
而不会导致整个 React 应用崩溃。
2. ErrorBoundary 能捕获哪些错误?
✅ 能捕获:
-
生命周期方法错误
-
render 渲染错误
-
constructor 错误
-
子组件错误
例如:
class Demo extends React.Component {
render() {
throw new Error('render error');
}
}
3. ErrorBoundary 不能捕获哪些错误?
❌ 事件函数错误
<button
onClick={() => {
throw new Error('点击异常');
}}
>
点击
</button>
不会进入 ErrorBoundary。
需要自己 try-catch:
const handleClick = () => {
try {
throw new Error('点击异常');
} catch (err) {
console.log(err);
}
};
❌ 异步代码错误
useEffect(() => {
setTimeout(() => {
throw new Error('异步错误');
}, 1000);
}, []);
不会被捕获。
应该:
useEffect(() => {
setTimeout(() => {
try {
throw new Error('异步错误');
} catch (err) {
console.log(err);
}
}, 1000);
}, []);
❌ Promise 错误
fetch('/api')
.then(res => res.json())
.catch(err => {
console.log(err);
});
需要手动 .catch()。
4. ErrorBoundary 实现原理
React 提供两个生命周期:
static getDerivedStateFromError
发生错误时更新状态。
static getDerivedStateFromError(error) {
return {
hasError: true
};
}
componentDidCatch
记录错误日志。
componentDidCatch(error, errorInfo) {
console.log(error);
console.log(errorInfo);
}
5. 完整实现
import React from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false
};
}
static getDerivedStateFromError(error) {
return {
hasError: true
};
}
componentDidCatch(error, errorInfo) {
console.error('错误信息:', error);
console.error('组件堆栈:', errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>页面发生异常</h1>;
}
return this.props.children;
}
}
export default ErrorBoundary;
使用:
<ErrorBoundary>
<UserInfo />
</ErrorBoundary>
6. React 18 + TypeScript 写法
import React, { Component, ReactNode } from 'react';
interface Props {
children: ReactNode;
}
interface State {
hasError: boolean;
}
class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
hasError: false
};
}
static getDerivedStateFromError(): State {
return {
hasError: true
};
}
componentDidCatch(error: Error, info: React.ErrorInfo) {
console.error(error);
console.error(info);
}
render() {
if (this.state.hasError) {
return <div>页面异常</div>;
}
return this.props.children;
}
}
export default ErrorBoundary;
7. 函数组件如何实现?
React 官方目前没有 Hook 版 ErrorBoundary。
函数组件通常借助:
安装:
npm install react-error-boundary
使用:
import { ErrorBoundary } from 'react-error-boundary';
function ErrorFallback({ error }) {
return (
<div>
<p>页面异常</p>
<pre>{error.message}</pre>
</div>
);
}
function App() {
return (
<ErrorBoundary FallbackComponent={ErrorFallback}>
<UserInfo />
</ErrorBoundary>
);
}
8. 项目中的最佳实践
全局错误边界
ReactDOM.createRoot(document.getElementById('root')).render(
<ErrorBoundary>
<App />
</ErrorBoundary>
);
页面级错误边界
<ErrorBoundary>
<PerformanceReport />
</ErrorBoundary>
模块级错误边界
<ErrorBoundary>
<EchartsPanel />
</ErrorBoundary>
这样即使图表崩溃:
<EchartsPanel />
也不会影响:
<Table />
<Form />
<Menu />
9. 云鉴性能平台中的应用场景
根据你目前的 React + Ant Design + ECharts 项目,可以这样使用:
<ErrorBoundary>
<PerformanceOverview />
</ErrorBoundary>
<ErrorBoundary>
<CpuCompareChart />
</ErrorBoundary>
<ErrorBoundary>
<IssueTracking />
</ErrorBoundary>
例如:
const data = undefined;
data.map(item => item.name);
出现:
Cannot read properties of undefined (reading 'map')
此时:
-
PerformanceOverview 崩溃
-
ErrorBoundary 显示兜底页面
-
其他模块正常显示
不会整页白屏。
10. 面试高频问题
Q:ErrorBoundary 是什么?
答:React 提供的错误捕获机制,用于捕获组件树中的运行时异常并展示降级 UI,避免整个应用崩溃。
Q:实现 ErrorBoundary 必须使用什么组件?
答:必须使用 Class Component,因为依赖:
static getDerivedStateFromError()
componentDidCatch()
函数组件不能直接实现。
Q:ErrorBoundary 能捕获事件错误吗?
答:不能。
onClick
onChange
setTimeout
Promise
async/await
中的异常需要自行处理。
一句话总结:
ErrorBoundary = React 的"try...catch(组件级)",专门用于捕获渲染阶段和生命周期中的错误,防止页面白屏。