大家好,我卡颂。 在很多全面使用Hooks开发的团队,唯一使用ClassComponent的场景就是「使用ClassComponent创建ErrorBoundary」。 可以说,如果Hooks存在如下两个生命周期函数的替代品,就能全面抛弃ClassComponent了: 那为什么还没有对标的Hook呢? 今天我们从上述两个生命周期函数的实现原理,以及要移植到Hook上需要付出的成本来谈论这个问题。 ErrorBoundary可以捕获子孙组件中「React工作流程」内的错误。 「React工作流程」指: 这也是为什么「事件回调中发生的错误」无法被ErrorBoundary捕获 —— 事件回调并不属于「React工作流程」。 「render阶段」的整体执行流程如下: do { try { // render阶段具体的执行流程 workLoop(); break; } catch (thrownValue) { handleError(root, thrownValue); } 可以发现,如果「render阶段」发生错误,会被捕获并执行handleError方法。 类似的云南idc服务商,「commit阶段」的整体执行流程如下: try { // ...具体执行流程 } catch (error) { captureCommitPhaseError(current, nearestMountedAncestor, error); 如果「commit阶段」发生错误,会被捕获并执行captureCommitPhaseError方法。getDerivedStateFromError原理。 我们知道,ClassComponent中this.setState第一个参数,除了可以接收「新的状态」,也能接收「改变状态的函数」作为参数: // 可以这样 this.setState(this.state.num + 1) // 也可以这样 getDerivedStateFromError的实现,就借助了this.setState中「改变状态的函数」这一特性。 当捕获错误后,即: 会在ErrorBoundary对应组件中触发类似如下更新: this.setState( getDerivedStateFromError.bind(null, error) 这就是为什么getDerivedStateFromError要求开发者返回「新的state」— 本质来说,他就是触发一次新的更新。 再来看另一个ErrorBoundary相关的生命周期函数— componentDidCatch。 ClassComponent中this.setState的第二个参数,可以接收「回调函数」作为参数: this.setState(newState, () => { // ...回调 当触发的更新渲染到页面后,回调会触发。云服务器提供商 这就是componentDidCatch的实现原理。 当捕获错误后,会在ErrorBoundary对应组件中触发类似如下更新: 可以发现,「React运行流程」中的错误,都已经被React自身捕获了,再交由ErrorBoundary处理。 如果没有定义ErrorBoundary,这些「被捕获的错误」需要重新抛出,营造「错误未被捕获的感觉」。 那这一步在哪里执行呢? 与this.setState类似,ReactDOM.render(element, container[, callback])第三个参数也能接收「回调函数」。 如果开发者没有定义ErrorBoundary,那么React最终会在ReactDOM.render的回调中抛出错误。 可以发现,在ClassComponent中ErrorBoundary的实现完全依赖了ClassComponent已有的特性。 而Hooks本身并不存在类似this.setState的回调特性,所以实现起来会比较复杂。 除了上述谈到的阻碍,FunctionComponent与ClassComponent在源码层面的运行流程也有细节上的差异,云服务器要照搬实现也有一定难度。 如果一定要实现,在「最大程度复用现有基础设施」的指导方针下,useErrorBoundary(ErrorBoundary在Hooks中的实现)的使用方式应该类似如下: function ErrorBoundary({ children}: { children: ReactNode}) { const [errorMsg, updateError] = useState useErrorBoundary((e: Error) => { // 捕获到错误,触发更新 updateError(e); }) return ( { errorMsg ? 报错: + errorMsg.toString() : children} ) 其中useErrorBoundary的触发方式类似useEffect: useErrorBoundary((e: Error) => { // ... }) // 类似 useEffect(() => { // ... 笔者仿照ClassComponent中ErrorBoundary的实现原理与useEffect的实现原理,实现了原生Hooks — useErrorBoundary。 感兴趣的朋友可以在useErrorBoundary在线示例[1]体验效果。 ErrorBoundary在ClassComponent中的实现使用了this.setState的回调函数特性,这使得Hooks中要完全实现同样功能,需要额外开发成本。 笔者猜测,这是没有提供对应原生Hooks的原因之一。 [1]useErrorBoundary在线示例:https://codesandbox.io/s/angry-mountain-xstgj4?file=/src/App.js。如何捕获错误
捕获后的错误如何处理呢?
componentDidCatch原理
处理“未捕获”的错误
总结