React 文档
导读与全书目录表:见同目录 01-React 入门:简介·特性与设计范式.md 开头。
React:错误边界(Error Boundary)
是什么
错误边界是类组件或通过 react-error-boundary 等库实现的组件:捕获 子树内 渲染、生命周期与构造函数中的错误,展示兜底 UI,避免整页白屏。
捕获范围
| 能捕获 | 不能捕获 |
|---|---|
| 子组件 render / 生命周期中的同步错误 | 事件处理器中的错误(需在回调内自行 try/catch) |
| 构造函数错误 | 异步代码(setTimeout / Promise)未关联到渲染的错误 |
| 服务端渲染单独流程(需 SSR 侧策略) |
事件里报错请使用 try/catch 或全局监听 window.onerror / unhandledrejection。
类组件写法要点
tsx
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, info) {
// 上报日志
}函数组件时代
官方暂无内置 Hook 等价物,常用 react-error-boundary 包装路由出口或页面级容器。
react 中如何捕获错误
回答
shell
React 错误捕获分三层:
其一是 逻辑 catch: 组件内可预测的同步逻辑用 try/catch,可预测的异步逻辑自身的 promise 用自身的 catch 方法,async/await 用try/catch包裹 await
其二是 错误边界组件:由于try/catch无法捕获子组件和渲染阶段异常,所以 React 16+ 引入了错误边界组件,专门捕获子组件异常(但是无法捕获自身异常,且只能用于 class 类组件)
其三是:全局未捕获错误用 window.onerror、window.addEventListener('unhandledrejection')
# 作用
错误捕获的核心目标:避免整个应用崩溃,显示友好降级 UI,同时记录错误日志便于排查。拓展:try/catch 只能捕获同步错误吗?那怎么能捕获 async/await?
shell
在 JavaScript 中,try/catch 只能捕获同步执行过程中抛出的错误,对于异步代码中的错误(例如在定时器回调、事件监听、Promise 链中抛出的错误),直接包在外层的 try/catch 是捕获不到的。
使用 try/catch 包裹 await 调用,这其实也是利用了 Promise 的 rejected 机制,但写法是同步风格,原理是 await 会把 Promise 的 rejected 状态转换成可 catch 的异常。
# 异步逻辑如何处理?
promise 用自身的 catch 方法
async/await 用try/catch包裹 await
定时器等异步逻辑 可以在定时器内部使用 try/catch 捕获react 错误边界
shell
# react 错误边界是什么?
错误边界是一种特殊的组件,它实现了两个特定的生命周期方法 static getDerivedStateFromError() - 用于渲染备用UI、componentDidCatch() - 用于记录错误信息
来捕获错误,记录这些错误,并显示备用 UI,而不是让整个组件树崩溃。
# 两个特定生命周期,通常一起使用,且分工明确
getDerivedStateFromError 负责捕获错误并更新 state,让 React 知道需要渲染降级 UI。
componentDidCatch 负责捕获错误并执行副作用,如日志记录。
错误边界可以捕获:子组件的 渲染错误、生命周期方法中的错误、构造函数中的错误(只能捕获子组件错误)
错误边界无法捕获:事件处理器内部的错误(需要使用普通 try/catch) 异步代码(如 setTimeout、requestAnimationFrame) 错误边界自身抛出的错误
# 代码
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null, errorInfo: null };
}
static getDerivedStateFromError(error) {
// 在渲染阶段调用,用于捕获子组件树抛出的错误,并更新 state 以触发降级 UI 的渲染。不允许副作用
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 在提交阶段调用,用于捕获错误并执行副作用,比如记录错误日志、上报错误信息到服务器等。允许副作用
console.error('捕获到错误:', error, errorInfo);
this.setState({ error, errorInfo });
}
render() {
if (this.state.hasError) {
// 自定义备用 UI
return (
<div className="error-boundary">
<h2>出错了!</h2>
<details style={{ whiteSpace: 'pre-wrap' }}>
<summary>查看错误详情</summary>
{this.state.error && this.state.error.toString()}
<br />
{this.state.errorInfo && this.state.errorInfo.componentStack}
</details>
</div>
);
}
return this.props.children;
}
}
// 使用方式
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>react 捕获错误
js是单线程语言,出现异常会使整个程序崩溃,所以需要捕获错误
三种方式
- 错误边界 Error Boundaries,用于捕获组件树中的 JavaScript 错误
shell
生命周期钩子:getDerivedStateFromError(从错误中获取派生状态)
生命周期钩子:componentDidCatch (组件已经捕获了异常)- 全局捕获
shell
捕获全局 JS 异常:window.addEventListener('error', (event) => { });- 组件内部错误处理
js
try/catch案例
- 封装成插槽:getDerivedStateFromError & componentDidCatch,发生错误展示错误提示,并上传错误日志
tsx
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染能够显示降级后的 UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 你可以将错误日志上报给服务器
logErrorToService(error, errorInfo);
}
render() {
if (this.state.hasError) {
// 你可以自定义降级后的 UI 并渲染
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
// 使用方式
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>- 应用于 App 根组件,作为全局异常捕获使用
tsx
// 在应用入口文件
class MyErrorBoundary extends React.Component {
// ... 同上
}
ReactDOM.render(
<MyErrorBoundary>
<App />
</MyErrorBoundary>,
document.getElementById('root')
);- 封装成高阶组件
ts
import { useState } from 'react';
// 注意:子组件 render 阶段抛错无法靠外层 try/catch 接住,需用「错误边界」类组件或 react-error-boundary
function withErrorHandling(WrappedComponent) {
return function WithErrorHandling(props) {
const [error, setError] = useState(null);
if (error) {
return <div>Error occurred: {error.message}</div>;
}
return <WrappedComponent {...props} onError={setError} />;
};
}注意
- getDerivedStateFromError 和 componentDidCatch 无法捕获:事件处理函数(onClick)、异步代码、服务端渲染,可使用 try catch 解决
- getDerivedStateFromError 和 componentDidCatch 无法捕获:自身组件抛出的异常(只能捕获子组件异常)解决方式:封装错误边界,让最外层容器组件成为其组件的子组件
