大家好,我卡颂。
在很多全面应用Hooks
开发的团队,惟一应用ClassComponent
的场景就是应用ClassComponent创立ErrorBoundary。
能够说,如果Hooks
存在如下两个生命周期函数的替代品,就能全面摈弃ClassComponent
了:
- getDerivedStateFromError
- componentDidCatch
那为什么还没有对标的Hook
呢?
明天咱们从上述两个生命周期函数的实现原理,以及要移植到Hook
上须要付出的老本来议论这个问题。
欢送退出人类高质量前端框架群,带飞
ErrorBoundary实现原理
ErrorBoundary
能够捕捉子孙组件中React工作流程内的谬误。
React工作流程指:
- render阶段,即组件render、Diff算法产生的阶段
- commit阶段,即渲染DOM、componentDidMount/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原理
捕捉后的谬误如何解决呢?
咱们晓得,ClassComponent
中this.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
。
ClassComponent
中this.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
的回调中抛出谬误。
能够发现,在ClassComponent
中ErrorBoundary
的实现齐全依赖了ClassComponent
已有的个性。
而Hooks
自身并不存在相似this.setState
的回调个性,所以实现起来会比较复杂。
实现Hooks中的ErrorBoundary
除了上述谈到的妨碍,FunctionComponent
与ClassComponent
在源码层面的运行流程也有细节上的差别,要照搬实现也有肯定难度。
如果肯定要实现,在最大水平复用现有基础设施的指导方针下,useErrorBoundary
(ErrorBoundary
在Hooks
中的实现)的应用形式应该相似如下:
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(() => { // ...})
笔者仿照ClassComponent
中ErrorBoundary
的实现原理与useEffect
的实现原理,实现了原生Hooks —— useErrorBoundary
。
感兴趣的敌人能够在useErrorBoundary在线示例体验成果。
总结
ErrorBoundary
在ClassComponent
中的实现应用了this.setState
的回调函数个性,这使得Hooks
中要齐全实现同样性能,须要额定开发成本。
笔者猜想,这是没有提供对应原生Hooks
的起因之一。