本节我们讲解Hooks的数据结构,为后面介绍具体的hook打下基础。

在上一节的极简useState实现中,使用isMount变量区分mountupdate

在真实的Hooks中,组件mount时的hookupdate时的hook来源于不同的对象,这类对象在源码中被称为dispatcher

可见,mount时调用的hookupdate时调用的hook其实是两个不同的函数。

FunctionComponent render前,会根据FunctionComponent对应fiber的以下条件区分mountupdate

  1. current === null || current.memoizedState === null

并将不同情况对应的dispatcher赋值给全局变量ReactCurrentDispatchercurrent属性。

FunctionComponent render时,会从ReactCurrentDispatcher.current(即当前dispatcher)中寻找需要的hook

当错误的书写了嵌套形式的hook,如:

  1. useEffect(() => {
  2. useState(0);
  3. })

此时ReactCurrentDispatcher.current已经指向ContextOnlyDispatcher,所以调用useState实际会调用throwInvalidHookError,直接抛出异常。

接下来我们学习hook的数据结构。

  1. memoizedState: null,
  2. baseState: null,
  3. baseQueue: null,
  4. queue: null,
  5. next: null,

其中除memoizedState以外字段的意义与上一章介绍的类似。

注意

  • fiber.memoizedStateFunctionComponent对应fiber保存的链表。

  • hook.memoizedStateHooks链表中保存的单一hook对应的数据。

不同类型hookmemoizedState保存不同类型数据,具体如下:

  • 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]

有些是没有memoizedState的,比如:

  • useContext