在上一节我们实现了一个极简的useState
,了解了Hooks
的运行原理。
本节我们讲解Hooks
的数据结构,为后面介绍具体的hook
打下基础。
# dispatcher
在上一节的极简useState
实现中,使用isMount
变量区分mount
与update
。
在真实的Hooks
中,组件mount
时的hook
与update
时的hook
来源于不同的对象,这类对象在源码中被称为dispatcher
。
// mount时的Dispatcher
const HooksDispatcherOnMount: Dispatcher = {
useCallback: mountCallback,
useContext: readContext,
useEffect: mountEffect,
useImperativeHandle: mountImperativeHandle,
useLayoutEffect: mountLayoutEffect,
useMemo: mountMemo,
useReducer: mountReducer,
useRef: mountRef,
useState: mountState,
// ...省略
};
// update时的Dispatcher
const HooksDispatcherOnUpdate: Dispatcher = {
useCallback: updateCallback,
useContext: readContext,
useEffect: updateEffect,
useImperativeHandle: updateImperativeHandle,
useLayoutEffect: updateLayoutEffect,
useMemo: updateMemo,
useReducer: updateReducer,
useRef: updateRef,
useState: updateState,
// ...省略
};
可见,mount
时调用的hook
和update
时调用的hook
其实是两个不同的函数。
在FunctionComponent
render
前,会根据FunctionComponent
对应fiber
的以下条件区分mount
与update
。
current === null || current.memoizedState === null
并将不同情况对应的dispatcher
赋值给全局变量ReactCurrentDispatcher
的current
属性。
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
你可以在这里看到这行代码
在FunctionComponent
render
时,会从ReactCurrentDispatcher.current
(即当前dispatcher
)中寻找需要的hook
。
换言之,不同的调用栈上下文为ReactCurrentDispatcher.current
赋值不同的dispatcher
,则FunctionComponent
render
时调用的hook
也是不同的函数。
除了这两个
dispatcher
,你可以在这里看到其他dispatcher
定义
# 一个dispatcher使用场景
当错误的书写了嵌套形式的hook
,如:
useEffect(() => {
useState(0);
})
此时ReactCurrentDispatcher.current
已经指向ContextOnlyDispatcher
,所以调用useState
实际会调用throwInvalidHookError
,直接抛出异常。
export const ContextOnlyDispatcher: Dispatcher = {
useCallback: throwInvalidHookError,
useContext: throwInvalidHookError,
useEffect: throwInvalidHookError,
useImperativeHandle: throwInvalidHookError,
useLayoutEffect: throwInvalidHookError,
// ...省略
你可以在这里看到这段逻辑
# Hook的数据结构
接下来我们学习hook
的数据结构。
const hook: Hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null,
};
你可以在这里看到创建
hook
的逻辑
其中除memoizedState
以外字段的意义与上一章介绍的updateQueue类似。
# memoizedState
注意
hook
与FunctionComponent fiber
都存在memoizedState
属性,不要混淆他们的概念。
fiber.memoizedState
:FunctionComponent
对应fiber
保存的Hooks
链表。hook.memoizedState
:Hooks
链表中保存的单一hook
对应的数据。
不同类型hook
的memoizedState
保存不同类型数据,具体如下:
useState:对于
const [state, updateState] = useState(initialState)
,memoizedState
保存state
的值useReducer:对于
const [state, dispatch] = useReducer(reducer, {});
,memoizedState
保存state
的值useEffect:
memoizedState
保存包含useEffect回调函数
、依赖项
等的链表数据结构effect
,你可以在这里看到effect
的创建过程。effect
链表同时会保存在fiber.updateQueue
中useRef:对于
useRef(1)
,memoizedState
保存{current: 1}
useMemo:对于
useMemo(callback, [depA])
,memoizedState
保存[callback(), depA]
useCallback:对于
useCallback(callback, [depA])
,memoizedState
保存[callback, depA]
。与useMemo
的区别是,useCallback
保存的是callback
函数本身,而useMemo
保存的是callback
函数的执行结果
有些hook
是没有memoizedState
的,比如:
- useContext