大家好,我卡颂。

在很多全面应用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的起因之一。