先了解一下工作原理,不过在那之前先说一下 React 工作流程

React 工作流程

  • render 阶段:render 组件、diff算法
  • commit 阶段:渲染真实 DOM

    ErrorBoundary 实现原理

    ErrorBoundary 就是捕获 React 工作流程中的错误 —— 不在工作流程中的就捕获不到 —— 比如 事件回调

捕获错误

  • 如果「render阶段」发生错误,会被捕获并执行handleError方法
  • 如果「commit阶段」发生错误,会被捕获并执行captureCommitPhaseError方法。

处理错误

ErrorBoundary 相关的有两个生命周期方法

getDerivedStateFromError

此生命周期会在后代组件抛出错误后被调用。 它将抛出的错误作为参数,并返回一个值以更新 state 更新 state 使下一次渲染可以显降级 UI

注意 getDerivedStateFromError() 会在渲染阶段调用,因此不允许出现副作用。 如遇此类情况,请改用 componentDidCatch()。

在捕获错误之后,会在 ErrorBoundary 组件中 触发更新:

  1. this.setState(
  2. getDerivedStateFromError.bind(null, error)
  3. )

this.setState第一个参数,除了可以接收「新的状态」,也能接收「改变状态的函数」作为参数

也就是 他就是触发了一次新的更新

componentDidCatch

此生命周期在后代组件抛出错误后被调用。 它接收两个参数:

  1. error —— 抛出的错误。
  2. info —— 带有 componentStack key 的对象,其中包含有关组件引发错误的栈信息

componentDidCatch() 会在“提交”阶段被调用,因此允许执行副作用。 它应该用于记录错误之类的情况:

this.setState的第二个参数,可以接收「回调函数」作为参数 这样可以保证应用更新立即触发

当捕获错误后,会在ErrorBoundary对应组件中触发类似如下更新:

  1. this.setState(this.state, componentDidCatch.bind(this, error))

为什么Hook 没

看上面就能发现,他这个 非常依赖 类组件的 setState 方法中的回调特性
而函数组件中的 useState 没有这些回调特性

实现一个试试

类组件 和 函数组件 在源码层面的运行流程也有差异 所以肯定不能完全复现