

为什么Hook没有ErrorBoundary?
source link: https://www.fly63.com/article/detial/11722
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

大家好,我卡颂。
在很多全面使用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的原因之一。
作者:卡颂,来源:https://segmentfault.com/a/1190000041974765
Recommend
-
36
爱奇艺 Android PLT hook 概述
-
44
git-commit-hook configureable git commit hook 1. Install Downlod the binary, put it into a folder of your $PATH (for example. /usr/local/bin). Ensure your user h...
-
34
以下包含相关所有的步骤,可根据现有环境进行跳过 安装JDK 安装Centos Jenkins和Github配置 配置任务 1.安装JDK Jenkins需要JDK才可以运行,我们首先安装J...
-
54
Metaprogramming: Ruby Hook Methods Ruby comes with a bunch of hook methods that allow you...
-
52
A minimalistic hook library based on PHP's Interface feature and PSR-11 container.
-
32
Picture this: you have a perfectly good function component, and then one day, you need to add a lifecycle method to it. Ugh.
-
30
There’s a running theme between all these new React Hooks : almost all of them e...
-
20
If you already read my previouspost we talk about how to create a simple todo with useState and useReducer . Today we’ll see how we can use useRef to refer to our elemen...
-
37
I recently updated a React state management library, hoping to reduce its API from two wrapper functions into a single hook. I did not succeed but I still ended up with something nice. Here is why and how. The s...
-
22
自从React 16.8发布Hook之后,笔者已经在实际项目中使用Hook快一年了,虽然Hook在使用中存在着一些坑,但是总的来说它是一个很好的功能,特别是在减少模板代码和提高代码复用率这些方面特别有用。为了让更多的人了解和使用Hook,我决定写...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK