# 不能在循环、条件或嵌套中调用Hooks
3 min read
Table of Contents
开发过程中,如果在条件语句中调用 hooks,React 会抛出错误。因为 hooks 的底层设计数据结构是链表,React 使用链表来保证 hooks 的顺序。
Hooks 调用链
一个典型的 useState 使用场景
const [name, setName] = useState('')
//...
setName('AB')Hooks 的调用链路在“首次渲染”和“更新阶段”是不同的。
首次渲染
在这个流程中,useState 触发的一系列操作最后会落到 mountState 里面去,所以需要重点关注的就是 mountState 做了什么事情。
function mountStateImpl(initialState) { // 将新的 hook 对象追加到链表尾部 const hook = mountWorkInProgressHook()
if (typeof initialState === 'function') { initialState = initialState() } hook.memoizedState = hook.baseState = initialState
// 创建当前 hook 对象的更新队列,主要是为了能够一次保留 dispatch const queue = { pending: null, lanes: NoLanes, dispatch: null, lastRenderedReducer: basicStateReducer, lastRenderedState: initialState, } hook.queue = queue return hook}
function mountState(initialState) { const hook = mountStateImpl(initialState) const queue = hook.queue const dispatch = dispatchSetState.bind(null, currentlyRenderingFiber, queue) queue.dispatch = dispatch return [hook.memoizedState, dispatch]}mounState 的主要工作是初始化 Hooks。最需要关注的是 mountWorkInProgressHook 方法,它为我们道出了 Hooks 背后的数据结构组织形式。
function mountWorkInProgressHook() { const hook = { memoizedState: null,
baseState: null, baseQueue: null, queue: null,
next: null, }
if (workInProgressHook === null) { // 链表中首个 hook currentlyRenderingFiber.memoizedState = workInProgressHook = hook } else { // 将 hook 插入到链表尾部 workInProgressHook = workInProgressHook.next = hook }
return workInProgressHook}hook 相关的所有信息收敛在一个 hook 对象里,而 hook 对象之间以“单向链表”的形式相互串联。
更新阶段
按顺序去遍历之前构建好的链表,取出对应的数据信息进行渲染。mountState(首次渲染)构建链表并渲染;updateState 依次遍历链表并渲染。
必须按照顺序调用从根本上来说是因为 useState 这个钩子在设计层面并没有“状态命名”这个动作,也就是说你每生成一个新的状态,React 并不知道这个状态名字叫啥,所以需要通过顺序来索引到对应的状态值