Skip to content

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是单线程语言,出现异常会使整个程序崩溃,所以需要捕获错误

三种方式

  1. 错误边界 Error Boundaries,用于捕获组件树中的 JavaScript 错误
shell
生命周期钩子:getDerivedStateFromError(从错误中获取派生状态)
生命周期钩子:componentDidCatch (组件已经捕获了异常)
  1. 全局捕获
shell
捕获全局 JS 异常:window.addEventListener('error', (event) => { });
  1. 组件内部错误处理
js
try/catch

案例

  1. 封装成插槽: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>
  1. 应用于 App 根组件,作为全局异常捕获使用
tsx
// 在应用入口文件
class MyErrorBoundary extends React.Component {
  // ... 同上
}

ReactDOM.render(
  <MyErrorBoundary>
    <App />
  </MyErrorBoundary>,
  document.getElementById('root')
);
  1. 封装成高阶组件
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} />;
  };
}

注意

  1. getDerivedStateFromError 和 componentDidCatch 无法捕获:事件处理函数(onClick)、异步代码、服务端渲染,可使用 try catch 解决
  2. getDerivedStateFromError 和 componentDidCatch 无法捕获:自身组件抛出的异常(只能捕获子组件异常)解决方式:封装错误边界,让最外层容器组件成为其组件的子组件