参考链接
面试回答
- 调用链路:
useState->mountState->mountWorkInProgressHook
- 从
mountWorkInProgressHook
函数源码中,可以看出- hook 对象之间以单向链表的形式相互串联
- 从
- 而后续状态更新(二次渲染)时执行的
updateState->updateReducer->updateWorkInProgressHook
函数updateState
调用了updateReducer
,所以更新时是把 state 当作/等同于 reducer 来处理的- 从
updateWorkInProgressHook
函数源码中,可以看出- 会按顺序去遍历之前构建好的链表,取出对应的数据信息进行渲染
- 注意这个过程就像从数组中依次取值一样,是完全按照顺序(或者说索引)来的
- 为什么用by order的方式,其他方案的问题
以 useState 为例,分析 React-Hooks 的调用链路
首次渲染
在首次渲染的流程中,useState
触发的一系列操作最后会落到 mountState
里面去,所以我们重点需要关注的就是 mountState
做了什么事情
mounState
的主要工作是初始化 Hooks。在整段源码中,最需要关注的是 mountWorkInProgressHook
方法,它为我们道出了 Hooks 背后的数据结构组织形式。以下是 mountWorkInProgressHook
方法的源码:
1 |
function mountWorkInProgressHook() { |
hook 相关的所有信息收敛在一个 hook 对象里,而 hook 对象之间以单向链表的形式相互串联。
更新(二次渲染)
首次渲染和更新渲染的区别,在于调用的是 mountState,还是 updateState。mountState 做了什么,你已经非常清楚了;而 updateState 之后的操作链路,虽然涉及的代码有很多,但其实做的事情很容易理解:按顺序去遍历之前构建好的链表,取出对应的数据信息进行渲染。
调用链路:updateState->updateReducer->updateWorkInProgressHook
下面代码比较复杂,建议跳过
1 |
//当前正在 update 的 fiber 上的 hook |
更新(二次渲染)的时候会发生什么事情:updateState 会依次遍历链表、读取数据并渲染。注意这个过程就像从数组中依次取值一样,是完全按照顺序(或者说索引)来的。因此 React 不会看你命名的变量名是 career 还是别的什么,它只认你这一次 useState 调用,于是它难免会认为:喔,原来你想要的是第一个位置的 hook 啊。
总结
hooks 的渲染是通过“依次遍历”来定位每个 hooks 内容的。如果前后两次读到的链表在顺序上出现差异,那么渲染的结果自然是不可控的。
这个现象有点像我们构建了一个长度确定的数组,数组中的每个坑位都对应着一块确切的信息,后续每次从数组里取值的时候,只能够通过索引(也就是位置)来定位数据。也正因为如此,Hooks 的本质其实是链表。