实例:
import React, { useState, useEffect } from react;import ReactDOM from react-dom;function Example() { const [count, setCount] = useState(0); useEffect(() => { setCount(7) setCount(9) }, []) const handleClick = () => {setCount(8) } return ( <div> <p>You clicked {count} times</p> <button onClick={handleClick}> Click me </button> </div> );}ReactDOM.render(<Example />, document.getElementById(root))首次执行函数组件,useState做了什么:
从全局对象ReactC在不同上下文使用不同的dispatcher。比如在不合法上下文调用hooks报错(此时dispatcher为ContextOnlyDispatcher),首次执行hooks和更新时执行hooks的dispatcher也是不同的。
首次执行useState会调用mountState
新建hook节点,workInProgressHook是null,此时hook是头节点,返回创建的hook
如果initialState是函数,则initialState是函数的返回值,可以实现惰性初始化。把initialState记录到hook的memoizedState和baseState。初始化更新队列,更新函数参数绑定根节点fiber和更新队列,同时更新函数保存在更新队列的dispatch中。最后返回memoizedState和更新方法。
首次执行函数组件,useEffect做了什么:
和useState相同,通过dispatcher调用hook,此时调用mountEffect。mountEffect调用mountEffectImpl(TODO:前两个参数作用未知)
同useState,创建hook节点,挂到hook单链表上。
设置hook的memoizedState是pushEffect的返回的effect对象。pushEffect创建一个循环链表的节点,把副作用函数create和依赖deps保存在effect节点中。componentUpdateQueue.lastEffect始终指向循环链表最后一个节点。
总结:
函数组件首次执行时,调用useState和useEffect实际上调用的是mountState和mountEffect。
mountState创建hook节点,把hook挂到hooks单链表上。把初始值和更新函数保存在hook节点上,更新函数绑定了根节点的fiber和更新队列。
mountEffect创建hook节点,把hook挂到hooks单链表上。创建effect节点,effect保存副作用函数和依赖,把effect加入到effect循环链表,hook的memoizedState保存这个effect。
bonus:循环链表相比单链表的优点?
循环链表相比单链表最大优点就是在任何一个节点都能遍历整个链表,单链表不能。
下篇文章分析执行setCount时发生了什么,非首次执行函数组件,useState和useEffect做了什么?