跳到主要内容

如何统一监听react组件报错

面试速答

  • 以下内容以 React 18 为准。
  • “统一监听 React 报错”不能只靠一种手段,通常要分成三层:
    • React 渲染错误:ErrorBoundary
    • 全局运行时错误:window.onerror
    • 异步错误:unhandledrejection
  • React 18 里如果使用 createRoot,还可以关注 onRecoverableError

第一层:组件树渲染错误

最核心的是错误边界 ErrorBoundary

它能捕获后代组件中的:

  • render 报错
  • 生命周期报错
  • 构造函数报错

典型写法:

class AppErrorBoundary extends React.Component<
{children: React.ReactNode},
{hasError: boolean}
> {
state = {hasError: false}

static getDerivedStateFromError() {
return {hasError: true}
}

componentDidCatch(error: Error, info: React.ErrorInfo) {
reportError({
error,
componentStack: info.componentStack,
})
}

render() {
if (this.state.hasError) return <div>页面异常</div>
return this.props.children
}
}

第二层:全局脚本错误

有些错误不在 React 渲染链路里,比如:

  • 第三方脚本异常
  • 手写原生事件回调异常
  • 资源加载错误

这时可以补:

window.addEventListener('error', event => {
reportError({type: 'error', event})
})

第三层:Promise 未处理异常

很多请求链路、异步逻辑会以 Promise 失败形式出现:

window.addEventListener('unhandledrejection', event => {
reportError({type: 'unhandledrejection', reason: event.reason})
})

React 18 的补充点

如果使用:

createRoot(container, {
onRecoverableError(error) {
reportError({type: 'recoverable', error})
},
})

可以监听一些可恢复错误,尤其在 hydration 等场景里有价值。

统一上报时要带什么

至少建议带:

  • 错误消息
  • 堆栈信息
  • componentStack
  • 当前路由
  • 用户信息或会话标识
  • 构建版本号

这样排查才真正有意义。

边界要讲清

ErrorBoundary 捕获不到:

  • 事件处理函数里的异常
  • setTimeout 里的异常
  • Promise 异步链未捕获异常
  • 服务端渲染阶段异常

所以它只能覆盖“组件渲染树的一部分错误”,不是万能兜底。

标准答法

问:如何统一监听 React 组件报错?

答:一般是 ErrorBoundary + window.onerror + unhandledrejection + 日志上报平台 的组合。React 18 如果是 createRoot,还可以补 onRecoverableError

易错点

  • 误以为 ErrorBoundary 能抓到所有异常。
  • 只采集错误消息,不带组件栈和版本信息。
  • 线上报错后没有降级 UI,用户直接白屏。

速记要点

  • 渲染错误靠 ErrorBoundary
  • 全局脚本错误靠 error
  • 异步 Promise 错误靠 unhandledrejection