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
在当前函数组件中, 每定义一个hooksAPI, 对应的, 会创建一个新的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节点