关于前端:为什么Hook没有ErrorBoundary

10次阅读

共计 2616 个字符,预计需要花费 7 分钟才能阅读完成。

大家好,我卡颂。

在很多全面应用 Hooks 开发的团队,惟一应用 ClassComponent 的场景就是 应用 ClassComponent 创立 ErrorBoundary

能够说,如果 Hooks 存在如下两个生命周期函数的替代品,就能全面摈弃 ClassComponent 了:

  • getDerivedStateFromError
  • componentDidCatch

那为什么还没有对标的 Hook 呢?

明天咱们从上述两个生命周期函数的实现原理,以及要移植到 Hook 上须要付出的老本来议论这个问题。

欢送退出人类高质量前端框架群,带飞

ErrorBoundary 实现原理

ErrorBoundary能够捕捉子孙组件中 React 工作流程 内的谬误。

React 工作流程 指:

  • render 阶段,即 组件 renderDiff 算法 产生的阶段
  • commit 阶段,即 渲染 DOMcomponentDidMount/Update 执行 的阶段

这也是为什么 事件回调中产生的谬误 无奈被 ErrorBoundary 捕捉 —— 事件回调并不属于React 工作流程

如何捕捉谬误

render 阶段 的整体执行流程如下:

do {
  try {
    // render 阶段具体的执行流程
    workLoop();
    break;
  } catch (thrownValue) {handleError(root, thrownValue);
  }
} while (true);

能够发现,如果 render 阶段 产生谬误,会被捕捉并执行 handleError 办法。

相似的,commit 阶段 的整体执行流程如下:

try {// ... 具体执行流程} catch (error) {captureCommitPhaseError(current, nearestMountedAncestor, error);
}

如果 commit 阶段 产生谬误,会被捕捉并执行 captureCommitPhaseError 办法。

getDerivedStateFromError 原理

捕捉后的谬误如何解决呢?

咱们晓得,ClassComponentthis.setState 第一个参数,除了能够接管 新的状态 ,也能接管 扭转状态的函数 作为参数:

// 能够这样
this.setState(this.state.num + 1)

// 也能够这样
this.setState(num => num + 1)

getDerivedStateFromError的实现,就借助了 this.setState扭转状态的函数 这一个性。

当捕捉谬误后,即:

  • 对于 render 阶段handleError 执行后
  • 对于 commit 阶段captureCommitPhaseError 执行后

会在 ErrorBoundary 对应组件中触发相似如下更新:

this.setState(getDerivedStateFromError.bind(null, error)
)

这就是为什么 getDerivedStateFromError 要求开发者返回 新的 state —— 实质来说,他就是触发一次新的更新。

componentDidCatch 原理

再来看另一个 ErrorBoundary 相干的生命周期函数 —— componentDidCatch

ClassComponentthis.setState 的第二个参数,能够接管 回调函数 作为参数:

this.setState(newState, () => {// ... 回调})

当触发的更新渲染到页面后,回调会触发。

这就是 componentDidCatch 的实现原理。

当捕捉谬误后,会在 ErrorBoundary 对应组件中触发相似如下更新:

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

解决“未捕捉”的谬误

能够发现,React 运行流程 中的谬误,都曾经被 React 本身捕捉了,再交由 ErrorBoundary 解决。

如果没有定义 ErrorBoundary,这些 被捕捉的谬误 须要从新抛出,营造 谬误未被捕捉的感觉

那这一步在哪里执行呢?

this.setState 相似,ReactDOM.render(element, container[, callback])第三个参数也能接管 回调函数

如果开发者没有定义 ErrorBoundary,那么React 最终会在 ReactDOM.render 的回调中抛出谬误。

能够发现,在 ClassComponentErrorBoundary的实现齐全依赖了 ClassComponent 已有的个性。

Hooks 自身并不存在相似 this.setState 的回调个性,所以实现起来会比较复杂。

实现 Hooks 中的 ErrorBoundary

除了上述谈到的妨碍,FunctionComponentClassComponent 在源码层面的运行流程也有细节上的差别,要照搬实现也有肯定难度。

如果肯定要实现,在 最大水平复用现有基础设施 的指导方针下,useErrorBoundaryErrorBoundaryHooks 中的实现)的应用形式应该相似如下:

function ErrorBoundary({children}: {children: ReactNode}) {const [errorMsg, updateError] = useState<Error | null>(null);

  useErrorBoundary((e: Error) => {
    // 捕捉到谬误,触发更新
    updateError(e);
  })

  return (
    <div>
      {errorMsg ? '报错:' + errorMsg.toString() : children}
    </div>
  )
}

其中 useErrorBoundary 的触发形式相似useEffect

useErrorBoundary((e: Error) => {// ...})

// 相似
useEffect(() => {// ...})

笔者仿照 ClassComponentErrorBoundary的实现原理与 useEffect 的实现原理,实现了原生 Hooks —— useErrorBoundary

感兴趣的敌人能够在 useErrorBoundary 在线示例体验成果。

总结

ErrorBoundaryClassComponent 中的实现应用了 this.setState 的回调函数个性,这使得 Hooks 中要齐全实现同样性能,须要额定开发成本。

笔者猜想,这是没有提供对应原生 Hooks 的起因之一。

正文完
 0