hook
作为FunctionComponent
最重要的机制, 当然要了解一下它是如何存储的.
更新
[2019-7-17]
- Initial release
前言
在分析hooks的运行机制过程中, 我通过一个简单的案例——如何使用hooks
? 来引申出在页面渲染阶段, hooks
是如何执行的.
当然, hooks
的执行分为两个阶段:
- mount 首次渲染阶段
- update 后续更新阶段
本篇笔记将记录有关:
- hooks是如何存储的
- useState-hooks的基本结构
附上一篇笔记的链接:
记录
注意: 以下记录的有关hooks
的内容, 均在mount
阶段执行
mountState
先来看一下React.useState
的主要源码部分:
展开源码
1 | function mountState<S>( |
mountWorkInProgressHook
在mountWorkInProgressHook
函数内部, 定义了hooks
在的存储方式. 之前有一个误区, 看了很多类似的对于React-Hooks
的分析文章, 都说hooks
是以数组
的形式存储的, 并对此深信不疑. 但是直到今天看了源码, 才恍然大悟, 可能由于版本变迁, 至少在react@16.8
版本, hooks
是以单向循环链表
的形式存储在fiber
上. 话不多说, 看一下源码:
展开源码
1 | function mountWorkInProgressHook(): Hook { |
hook
在当前函数
组件中, 每定义一个hooks
API, 对应的, 会创建一个新的hook
对象, 看一下hook
的类型定义:
展开源码
1 | export type Hook = { |
hook
结构的几个重要的属性, 在mount
阶段, 我并没有看懂, 转而看了一遍update
的流程, 也就是在React.useState
中, 自行调用dispatch
来更新state
, 之后才初步理解:
- memoizedState
- baseState
- baseUpdate
- queue
- next
hook.memoizedState
顾名思义, 在React.useState
中, 保存计算后的新state
, 也就是下述代码返回的newState
1 | const [newState, dispatch] = React.useState(initialState); |
hook.baseState
在遍历updateQueue
的过程中, 如果遍历到的update
的expirationTime
小于整体更新的expirationTime
, 表明当前的hooks
产生的update的优先级较小, 不会在此次更新流程中执行, 从而导致被中断. 此时hook.baseState
则保存上一次的update
计算出来的state
.
hook.baseUpdate
同baseState
一样, baseUpdate
保存上一次被中断的更新的上一个update
, 等到下一次renderRoot
时, 先从baseUpdate
开始.
hook.queue
hook.queue
与class
组件的updateQueue
相似, 我姑且把它当成updateQueue
吧, 不同的是:
在
class
组件中,updateQueue
保存的是整个类产生的更新
而在function
组件中,updateQueue
保存的是单个hooks
产生的更新, 此时可能有多个hooks
再来看一下queue
的结构:
1 | type UpdateQueue<S, A> = { |
每个hooks
产生的更新:
1 | type Update<S, A> = { |
hook.next
下一个hook
节点