错误边界
本教程共 50 篇 · 第 38 篇 · 更新于 2026-07-29 · 约 7 分钟阅读
38. 错误边界
本节目标:学会用错误边界捕获组件渲染时的报错,防止整个应用白屏,给用户展示友好的错误提示。
没有错误边界的后果
一个组件渲染时抛出异常,如果没有被捕获,整个 React 树都会卸载。用户看到的就是白屏,没有任何提示。
function BuggyComponent() {
throw new Error('我崩了');
return <p>这行永远不会执行</p>;
}
function App() {
return (
<div>
<h1>我的应用</h1>
<BuggyComponent /> {/* 💥 整个 App 白屏 */}
</div>
);
}
错误边界就是来解决这个问题的——捕获子组件树的渲染错误,显示降级 UI,而不是崩溃整棵树。
错误边界只能用类组件写
React 官方规定:错误边界必须用类组件。目前还没有”函数组件版”的错误边界 API。
import { Component } from 'react';
class ErrorBoundary extends Component {
state = { hasError: false, error: null };
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
console.error('捕获到错误:', error, errorInfo);
// 这里可以上报错误到监控服务
}
render() {
if (this.state.hasError) {
return this.props.fallback;
}
return this.props.children;
}
}
两个关键生命周期方法:
getDerivedStateFromError:收到错误后更新 state,渲染降级 UI。必须是纯函数,不能有副作用。componentDidCatch:在这里做日志上报、错误记录等副作用。
使用方式:
function App() {
return (
<ErrorBoundary fallback={<p>出错了,请刷新重试</p>}>
<BuggyComponent />
</ErrorBoundary>
);
}
Note错误边界只能捕获渲染期间的报错。事件处理、异步代码(setTimeout、fetch 回调)、服务端渲染的错误它捕获不到。
错误边界的捕获范围
错误边界能捕获的:
- 子组件渲染时抛出的异常
- 子组件生命周期方法中的报错(类组件)
- 子组件构造函数中的报错
不能捕获的:
- 事件处理函数里的报错(用 try/catch)
- 异步代码里的报错
- 服务端渲染的错误
- 错误边界自身的报错
function App() {
return (
<ErrorBoundary fallback={<p>页面出错了</p>}>
<Header />
<ErrorBoundary fallback={<p>列表出错了</p>}>
<List />
</ErrorBoundary>
<ErrorBoundary fallback={<p>侧边栏出错了</p>}>
<Sidebar />
</ErrorBoundary>
</ErrorBoundary>
);
}
可以嵌套使用,每个边界只管自己的子树。List 崩了不影响 Sidebar。
react-error-boundary:函数组件的救星
每次写类组件太烦了。社区有个 react-error-boundary 库,让你用函数组件的方式写错误边界:
npm install react-error-boundary
import { ErrorBoundary } from 'react-error-boundary';
function App() {
return (
<ErrorBoundary
fallbackRender={({ error, resetErrorBoundary }) => (
<div>
<p>出错了:{error.message}</p>
<button onClick={resetErrorBoundary}>重试</button>
</div>
)}
onError={(error, info) => {
// 上报错误
reportError(error, info);
}}
>
<BuggyComponent />
</ErrorBoundary>
);
}
fallbackRender 比 fallback 更灵活,能拿到错误对象和重置函数。
Tip
resetErrorBoundary会重置错误状态,重新渲染子组件。用户点”重试”就能再试一次。
配合 Suspense 使用
React 19 的 use(Promise) 如果 Promise 被拒绝,错误会冒泡到最近的错误边界:
import { use, Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
function Message({ messagePromise }) {
const content = use(messagePromise);
return <p>{content}</p>;
}
function App() {
return (
<ErrorBoundary fallback={<p>加载失败</p>}>
<Suspense fallback={<p>加载中……</p>}>
<Message messagePromise={fetchMessage()} />
</Suspense>
</ErrorBoundary>
);
}
Suspense 处理”加载中”,错误边界处理”加载失败”。各司其职。
错误边界的最佳实践
- 放在路由级别:每个路由页面包一个错误边界,一个页面崩了不影响其他页面
- 放在关键组件外:列表、卡片、侧边栏这些独立模块各自包一个
- 提供重试机制:用
resetErrorBoundary让用户可以手动重试 - 上报错误:在
componentDidCatch或onError里把错误发到监控平台
// 路由级别的错误边界
function App() {
return (
<Routes>
<Route path="/" element={
<ErrorBoundary fallback={<HomeError />}>
<HomePage />
</ErrorBoundary>
} />
<Route path="/dashboard" element={
<ErrorBoundary fallback={<DashboardError />}>
<DashboardPage />
</ErrorBoundary>
} />
</Routes>
);
}
Warning不要给每个小组件都包错误边界。错误边界是”最后防线”,不是常规错误处理。业务逻辑里的错误该 try/catch 就 try/catch。
事件处理里的错误
事件处理函数里的报错,错误边界捕获不到。你需要自己处理:
function DeleteButton() {
const [error, setError] = useState(null);
async function handleClick() {
try {
await deleteItem();
} catch (err) {
setError(err.message);
}
}
if (error) return <p>删除失败:{error}</p>;
return <button onClick={handleClick}>删除</button>;
}
或者用 state 配合错误边界:在事件处理里 setState 触发渲染错误,错误边界就能捕获了。但这不是推荐做法,还是 try/catch 更直接。