错误边界实践进阶
学习目标
- 明确错误边界「能捕获 / 不能捕获」的范围
- 掌握嵌套错误边界的隔离策略
- 理解错误边界在 RSC / Actions 时代的补充手段
- 掌握函数组件借助封装类组件获得错误边界的方案
能捕获什么
错误边界(通过 componentDidCatch / getDerivedStateFromError 实现的类组件)只捕获渲染期、生命周期、构造函数中抛出的错误,对以下情况无效:
- 事件处理函数里的错误(需用 try/catch);
- 异步代码(如
setTimeout、fetch回调)中的错误; - 服务端渲染时的错误;
- 错误边界自身的错误。
jsx
class EB extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() { return { hasError: true }; }
componentDidCatch(error, info) {
logError(error, info.componentStack); // 上报错误堆栈
}
render() { return this.state.hasError ? this.props.fallback : this.props.children; }
}注意:错误边界目前只能是类组件,函数组件尚无等价 API(社区常封装一个 <ErrorBoundary> 类组件供函数组件使用)。
函数组件如何拥有错误边界
虽然 React 未提供 useErrorBoundary Hook,但可把上面的类组件封装成一个接受 fallback 与 onError 的通用边界组件,供函数组件以声明式方式使用:
jsx
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() { return { hasError: true }; }
componentDidCatch(error, info) { this.props.onError?.(error, info); }
render() { return this.state.hasError ? this.props.fallback : this.props.children; }
}
// 函数组件里直接包一层
function Page() {
return (
<ErrorBoundary fallback={<p>出错了</p>} onError={logError}>
<RiskyWidget />
</ErrorBoundary>
);
}若想在函数组件内部主动触发边界(例如把 async 错误抛给边界而非自己吞掉),可借助一个「抛出型」子组件:
jsx
function Throw({ error }) { if (error) throw error; return null; }
// <ErrorBoundary><Throw error={loadError} /></ErrorBoundary>注意:这仍是「类组件兜底 + 函数组件调用」的模式,React 官方尚无把 componentDidCatch 直接函数化的计划,因此封装类组件是当下唯一可靠方案。
嵌套隔离策略
把错误边界细粒度包裹在「可独立失败」的区域,避免一个小组件崩溃拖垮整个页面:
jsx
<Layout>
<EB fallback={<NavError />}><Nav /></EB>
<EB fallback={<FeedError />}><Feed /></EB>
</Layout>与 RSC / Actions 的配合
- 服务端组件报错:由框架层
error.tsx边界(客户端 ErrorBoundary)兜底,详见 12-Next.js App Router 数据加载; - Server Action 失败:
useActionState的返回值中带error,在表单内局部展示,无需错误边界; - 客户端事件错误:仍用
try/catch,错误边界管不到。
总结
错误边界是「渲染期崩溃的防火墙」,但范围有限——事件/异步错误要靠 try/catch 与 Action 返回值处理。细粒度嵌套 + 错误上报才能让生产环境可观测。