Error Boundary 是个啥?
就是 React 版的"try-catch",专门用来抓渲染时的错误
先说结论:Error Boundary 是一个特殊的组件,它可以捕获子组件树里的错误,防止整个页面炸掉。
怎么实现?
必须是 Class 组件,用两个生命周期:
static getDerivedStateFromError(error) — 渲染降级 UI
componentDidCatch(error, errorInfo) — 记录错误日志
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
logErrorToMyService(error, errorInfo); // 上报监控
}
render() {
if (this.state.hasError) {
return <h1>出错了,请稍后再试</h1>;
}
return this.props.children;
}
}
用法:
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
能捕获哪些错误?
- 渲染期间的错误
- 生命周期里的错误
- 构造函数里的错误
- 子组件里的错误
不能捕获哪些错误?(重点)
1. 事件处理器里的错误
function MyButton() {
const handleClick = () => {
throw new Error("click error"); // 不会被捕获!
};
return <button onClick={handleClick}>点我</button>;
}
为什么不能捕获?
因为 React 的 Error Boundary 是在 渲染阶段 起作用。事件处理器里的代码是在事件触发后才执行的,这时候渲染已经完成了。
更重要的是:事件处理器的错误不会"破坏"渲染结果。点击一下报错了,但 React 依然可以把失败后的界面画出来。Error Boundary 的职责是"渲染出错了,页面保不住",而不是"某个交互失败了"。
正确做法:用 try/catch 包裹:
const handleClick = () => {
try {
// 可能出错的操作
} catch (e) {
console.error(e);
}
};
2. 异步代码里的错误
function MyComponent() {
useEffect(() => {
setTimeout(() => {
throw new Error("timeout error"); // 不会被捕获!
}, 1000);
}, []);
return <div>组件</div>;
}
为什么不能捕获?
setTimeout、Promise.then()、requestAnimationFrame 这些都是"在 React 渲染之外"执行的代码。它们由浏览器或 Node.js 调度,React 根本不知道它们的存在。
当这些异步任务抛出错误时,React 已经完成渲染了,没有任何人能"接住"这个错误。
正确做法:
// Promise 用 .catch
fetch("/api")
.then()
.catch((err) => console.error(err));
// setTimeout 在内部 try/catch
setTimeout(() => {
try {
// 可能出错
} catch (e) {
console.error(e);
}
}, 1000);
// 或者用 unhandledrejection 事件监听
window.addEventListener("unhandledrejection", (e) => {
console.error(e.reason);
});
3. Error Boundary 自身的错误
class ErrorBoundary extends React.Component {
render() {
throw new Error("边界自己炸了"); // 会被上层的边界捕获!
}
}
<ErrorBoundary>
{/* 这个边界自己错了,会冒泡到更外层的边界 */}
<ErrorBoundary>
<Child />
</ErrorBoundary>
</ErrorBoundary>;
为什么不能自己捕获自己?
如果 Error Boundary 在 getDerivedStateFromError 或 componentDidCatch 里也报错了,它自己是没办法处理的——因为它正在"处理错误"中,没法再处理"处理错误时的错误"。
这时候错误会继续向上冒泡,直到被上层的 Error Boundary 捕获。
4. 服务端渲染 (SSR)
// 服务端渲染时,Error Boundary 不生效
// 因为 SSR 没有"组件树"的概念,错误会直接导致整个服务崩溃
为什么不能捕获?
SSR 是在 Node.js 里"一次性"渲染 HTML,没有"渲染-错误-降级"这个过程。渲染过程中任何错误都会直接抛出,导致整个请求失败。
正确做法: SSR 时在服务端用 try/catch 包裹,或者用框架提供的错误边界(如 Next.js 的 error.tsx)。
总结
| 能捕获 | 不能捕获 | 为什么 |
|---|
| 渲染错误 | onClick 错误 | 事件处理器在渲染后执行,错误不破坏渲染结果 |
| 生命周期错误 | 异步代码错误 | setTimeout/Promise 在 React 渲染之外执行 |
| 子组件错误 | 自身错误 | 自身错误会冒泡到上层边界 |
| SSR | SSR 没有组件树,错误直接导致请求失败 |
记住:Error Boundary 只管渲染方面的错误,业务逻辑的错误还得靠 try/catch。