React 源码探源 4 useState
2021-06-28 本文已影响0人
吴摩西
综述
书接上一章节: React 源码探源 3 Update 的整体流程. 原有的代码和相关的流程图不在赘述。本节来细究一下 useState 相关的流程。
fiber 与 useState hook 结构
fiber 与 useState hook 结构有图中可以看到,一个组件的所有 hook
都存储在 fiber
的 memorizedState
下面的队列中,hook 按照出现的顺序通过 next
存储成单向链表。每个 hook
中会存储当前需要执行的 update
所形成的单向循环链表。
update
的结构如下:
-
pending
: 指向下一个更新,因为是循环链表,链表尾会指向链表头。 -
action
: 需要执行的更新, 即setState
传递的响应函数。 -
eagerState
: 如果组件没有更新过,会在执行setState
时计算下一个state
的值,否则会在执行下一次
的render
组件时的useState
时才会计算。
初始化过程
render 和更新流程初次渲染,当调用
useState
时,会调用 HooksDispatcherOnMountInDEV
的 mountState
,返回两个值,第一个是 hook.memorizedState
,即当前 hook 的值。第二个是 dispatchAction
,绑定三个参数
-
action
,setState
的回调函数,初始化时为 null. -
queue
, 当前hooks
挂载的更新队列. -
fiber
, 当前的 fiber.
更新过程
当事件触发后,会调用事件响应中的 setState
函数,实际上是初始化时生成的第二个返回值 dispatchAction
,执行后标记改组件有所更新,详情请参考上一节。当再次执行到 fiber(Dev)
时,会调用 HooksDispatcherOnUpdateInDEV
的 updateState
函数,会执行更新队列中剩余的更新。
state
更新后,其自组件再根据传递的 props
不同重新渲染。
scheduleMicrotask
通过通读代码发现, flushSyncCallbacks
可能会通过 scheduleMicrotask
执行。即在本次调用栈清空以后,下一次浏览器渲染之前执行。观察源码可以发现会按照以下三种方式进行尝试
-
queueMicrotask,可以看到在浏览器兼容的情况下, 此 API 对
microtask
支持的最好 -
Promise.resolve(null).then(callback)
,容易得知,Promise.then
的回调会向micotask
队列推送任务。 -
setTimeout(callback, 0)
,浏览器会选中增加setTimeout
回调的事件,造成不必要的延迟。