关于 web-error-handling-error-boundaries
面向 React 开发的错误边界技能,用于隔离渲染错误并提供备用界面与恢复路径。内容涵盖类边界、react-error-boundary、异步错误路由和 React 19 根级报告。
主要功能
- 01基于类的错误边界
- 02备用界面与重置控件
- 03通过 showBoundary() 传递异步错误
- 04细粒度边界布局
- 05React 19 根级错误报告
适用场景
- 01避免组件故障导致整页替换
- 02为渲染错误添加重试控件
- 03将异步故障传入备用界面
- 04集中报告 React 19 根级错误
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ agents-inc/ skills --skill web-error-handling-error-boundaries安装后在 Claude Code 中输入/web-error-handling-error-boundaries确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
metadata.yamlreference.mdexamples/core.mdreact-19-hooks.mdrecovery.mdtesting.md详细介绍
React 错误边界
快速指南: 边界会捕获渲染期间、生命周期方法中以及构造函数中抛出的错误,并将子树替换为备用 UI。它无法捕获事件处理器、异步操作或服务端渲染中的错误——只有在有代码调用
showBoundary()时,这些错误才会传递给它。边界必须是类组件,因为getDerivedStateFromError和componentDidCatch没有对应的 Hook。React 19 在createRoot中新增了onCaughtError、onUncaughtError和onRecoverableError,用于记录错误,而非渲染 UI;React 18 会静默忽略这些选项。
详细资源:
- examples/core.md — 类边界、
react-error-boundary用法、resetKeys、useErrorBoundary、精细化放置 - examples/react-19-hooks.md —
createRoot/hydrateRoot错误选项、captureOwnerStack()、错误筛选 - examples/recovery.md — 重试限制、指数退避、错误分类
- examples/testing.md — 如何让边界可测试,以及实现测试的固定样例
- reference.md — 边界能捕获哪些错误、生命周期和属性表、检查清单
适用路径
- 不想引入边界依赖 — 自行编写类组件;
getDerivedStateFromError加上componentDidCatch就是完整 API,examples/core.md 中有完整示例。 - 可使用
react-error-boundary— 直接使用resetKeys、useErrorBoundary和FallbackProps类型,而不是重新实现;请参阅 examples/core.md。 - 使用 React 19,且关注的是记录日志而非 UI — 这三个
createRoot选项会报告 所有错误,包括边界未能捕获的错误;请参阅 examples/react-19-hooks.md。
<critical_requirements>
编写错误边界代码前
从 getDerivedStateFromError 返回新状态,并将所有副作用放在 componentDidCatch 中。 前者在渲染期间运行,此时发起请求或调用日志函数会违反 React 的阶段规则;后者在提交阶段运行,此时执行这些操作是安全的。
除了根节点外,还要为每个功能区域分别添加边界。 单个根边界会让一个出错的小部件变成空白页面,而且只有将回退界面放在出错组件旁边,它才能说明具体哪里出了问题。
为回退界面提供恢复途径——重置回调、resetKeys,或两者兼备。 如果没有恢复途径,用户只能完整刷新页面,这会丢失他们输入的所有内容。
在回退界面上设置 role="alert",并使用真正的按钮作为控件。 否则子树消失时不会有任何提示,屏幕阅读器用户也无法得知发生了错误。
通过 showBoundary() 处理异步错误和事件处理程序中的错误。 边界无法捕获被拒绝的 Promise,因此未处理的 Promise 会让 UI 显示过时内容,且完全没有错误状态。
</critical_requirements>
自动检测: error boundary、ErrorBoundary、getDerivedStateFromError、componentDidCatch、fallback UI、react-error-boundary、useErrorBoundary、showBoundary、error fallback、onCaughtError、onUncaughtError、onRecoverableError、captureOwnerStack、FallbackProps、resetKeys
适用范围:
- 捕获渲染阶段的错误,并显示回退 UI 替代出错内容
- 捕获错误后的重置与重试,包括重试次数限制和退避策略
- 决定边界的放置位置,以及每个边界的覆盖范围
- 从 React 根节点集中报告错误
其他内容:
- 服务端渲染中抛出的错误——渲染框架决定渲染失败时向客户端发送什么,而此时客户端边界尚未挂载。
- 数据层中的请求失败——只有在某处重新抛出错误或调用
showBoundary()时,边界才能看到它;重试和缓存失效由发起请求的部分负责。 - 字段级验证反馈——无效的表单字段属于预期输入,因此应以内联方式渲染,而不是替换整个子树。
- 监控目标——
onError和根节点处理程序会将错误和组件堆栈交给你,如何处理这些信息属于报告工具的职责。
理念
如果没有边界捕获渲染错误,整个树都会卸载:React 会拆除根节点,而不是让屏幕上保留只渲染了一半的文档,因此任何位置抛出的一个错误都会变成空白页面。
边界能提供隔离:影响范围是最近边界包裹的子树,因此边界的位置决定了单个错误会让页面损失多少内容。这使边界的放置成为真正需要考虑的决策——恢复方式、回退提示文案和日志记录都由此而定。
边界不能替代 try/catch;它们负责覆盖 try/catch 无法触及的区域,也就是 React 自身的渲染过程。
核心模式
模式 1:基于类的边界
这两个生命周期方法按阶段分工:getDerivedStateFromError 是纯函数,只返回状态;报告错误则放在 componentDidCatch 中。
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
this.props.onError?.(error, errorInfo);
}
完整代码:examples/core.md
模式 2:react-error-boundary
FallbackComponent 会接收 error 和 resetErrorBoundary,因此可以根据设计需要将重试控件放在任意位置。onError 则将错误报告逻辑与回退界面分开。
<ErrorBoundary FallbackComponent={ErrorFallback} onError={report}>
<Dashboard />
</ErrorBoundary>;
function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
return (
<div role="alert">
<p>{error.message}</p>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
);
}
属性表见 reference.md;完整代码见 examples/core.md。
模式 3:使用 showBoundary() 处理异步错误
异步操作中抛出的错误不会自行传递到边界。useErrorBoundary 会提供触发函数。
const { showBoundary } = useErrorBoundary();
const load = async () => {
try {
const response = await fetch(endpoint);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
} catch (error) {
showBoundary(error);
}
};
如果错误发生时需要替换整个子树,就在相应位置使用它。如果错误有自己的内联 UI(例如字段级验证消息),则不要使用它。
完整代码:examples/core.md
模式 4:用于自动重置的 resetKeys
任何列出的值发生变化,都会清除错误并重新渲染子组件,这正是导航和切换记录所需要的行为。
<ErrorBoundary FallbackComponent={ErrorFallback} resetKeys={[currentPath]}>
<PageContent path={currentPath} />
</ErrorBoundary>
比较是浅层的,因此对象或数组形式的键需要保持引用稳定,否则边界会在每次渲染时重置。
模式 5:精细放置
每个组件都有自己的边界和回退文本;根边界则捕获内部边界未捕获的错误。
<ErrorBoundary fallback={<p>Chart unavailable</p>} onError={report}>
<ChartWidget />
</ErrorBoundary>
<ErrorBoundary fallback={<p>Table unavailable</p>} onError={report}>
<DataTable />
</ErrorBoundary>
用一个边界包裹全部三个组件,会导致第一个错误发生时,另外两个也随之消失。
边界有一个上限:其单元应是用户能够识别为独立故障的功能区域。每个组件都设置边界,会增加用户根本不会看的类组件和回退文本,还会把一个故障拆成一页小型损坏面板,而不是显示一条直白的信息。
完整代码:examples/core.md
模式 6:回退 UI
回退 UI 是用户在最糟糕时刻看到的界面:告知用户发生了什么,提供脱困方式,并将堆栈跟踪保留给开发环境。
<div role="alert">
<h2>Something went wrong</h2>
{isDevelopment && <pre>{error.message}</pre>}
<button onClick={resetErrorBoundary}>Try again</button>
</div>
<span onClick> 重试操作无法通过键盘触发,而在生产环境中显示原始堆栈则会泄露内部信息。
完整代码:examples/core.md
模式 7:React 19 根错误选项
createRoot 提供三个处理程序,用于报告错误而非渲染错误,包括未被任何边界捕获的错误。它们与边界互为补充,而不是用来替代边界。
const root = createRoot(container, {
onCaughtError: (error, info) => report("caught", error, info.componentStack),
onUncaughtError: (error, info) =>
report("uncaught", error, info.componentStack),
onRecoverableError: (error, info) =>
report("recoverable", error, info.componentStack),
});
完整代码:examples/react-19-hooks.md
<red_flags>
注意事项
运行时会出错:
- 在
getDerivedStateFromError中执行副作用——它在渲染期间运行,因此在其中发起请求或调用setState会违反 React 的阶段规则;应改为在componentDidCatch中报告。 - 边界包裹自身——边界无法捕获自身渲染错误,抛出的错误会传递给父边界或根节点;应让回退 UI 保持简单。
- 不稳定的
resetKeys条目——浅层比较会发现每次渲染都产生了新的数组或对象,并持续重置边界;请对该值使用 memo,或将键设为原始类型。 - 使用
<span onClick={reset}>作为重试控件——无法获得焦点,也无法通过 Enter 或 Space 激活;请使用<button>。
行为可能出乎意料:
- 异步错误和事件处理程序中的错误永远不会传递给边界;如果没有
showBoundary(),它们就会消失。 - 最内层边界优先,因此只有所有内层边界都未捕获错误时,才会显示范围较大的根回退 UI。
- SSR 水合错误会作为可恢复错误而非已捕获错误出现,客户端边界可能永远看不到它们。
- 开发环境热重载可能触发生产环境中不会出现的边界。
onCaughtError在边界自身的componentDidCatch之后运行,而不是之前。onRecoverableError中的error.cause通常包含原始错误。- 在开发环境之外,
captureOwnerStack()会返回null。 - 在 React 18 中,三个根选项会被静默忽略——既不会警告,也不会报错或记录日志。
</red_flags>