什么是 Hook?
Hook 是一个特殊的函数, 它可以让你“钩入” React 的特性. 如, useState 是允许你在 React 函数组件中添加 state 的 Hook.
在 v17.0.2 中, 共定义了 14 种 Hook
1export type HookType =2 | "useState"3 | "useReducer"4 | "useContext"5 | "useRef"6 | "useEffect"7 | "useLayoutEffect"8 | "useCallback"9 | "useMemo"10 | "useImperativeHandle"11 | "useDebugValue"12 | "useDeferredValue"13 | "useTransition"14 | "useMutableSource"15 | "useOpaqueIdentifier";
Hook 数据结构
1type Update<S, A> = {2 lane: Lane,3 action: A,4 eagerReducer: ((S, A) => S) | null,5 eagerState: S | null,6 next: Update<S, A>,7 priority?: ReactPriorityLevel,8};910type UpdateQueue<S, A> = {11 pending: Update<S, A> | null,12 dispatch: (A => mixed) | null,13 lastRenderedReducer: ((S, A) => S) | null,14 lastRenderedState: S | null,15};1617export type Hook = {18 memoizedState: any, // 当前状态19 baseState: any, // 基状态20 baseQueue: Update<any, any> | null, // 基队列21 queue: UpdateQueue<any, any> | null, // 更新队列22 next: Hook | null, // next指针23};
- hook.memoizedState: 保持在内存中的局部状态.
- hook.baseState: hook.baseQueue 中所有 update 对象合并之后的状态.
- hook.baseQueue: 存储 update 对象的环形链表, 只包括高于本次渲染优先级的 update 对象.
- hook.queue: 存储 update 对象的环形链表, 包括所有优先级的 update 对象.
- hook.next: next 指针, 指向链表中的下一个 hook.
Hook 与 Fiber
使用 Hook 最终也是为了控制 fiber 节点的状态和副作用
1export type Fiber = {2 // 1. fiber节点自身状态相关3 pendingProps: any;4 memoizedProps: any;5 updateQueue: mixed;6 memoizedState: any;78 // 2. fiber节点副作用(Effect)相关9 flags: Flags;10 nextEffect: Fiber | null;11 firstEffect: Fiber | null;12 lastEffect: Fiber | null;13};
使用 Hook 的任意一个 api, 最后都是为了控制上述这几个 fiber 属性.
我们之前有大概了解了 Fiber,那么看下 Fiber 和 Hook 有什么关系吧,在这之前我们还是以一个组件做示例
1function App() {2 // 1. useState3 const [count, setCount] = useState(0);4 // 2. useEffect5 useEffect(() => {6 console.log(`effect 1 created`);7 });8 // 3. useState9 const [name, setName] = useState("John");10 // 4. useEffect11 useEffect(() => {12 console.log(`effect 2 created`);13 });14 return (15 <>16 <h117 onClick={() => {18 setCount(() => count + 1);19 }}20 >21 <p title={count}>{count}</p> {name}22 </h1>23 </>24 );25}
在这个 function 组件中, 同时使用了状态 Hook 和副作用 Hook.
初次渲染时, 逻辑执行到
performUnitOfWork
->beginWork
->updateFunctionComponent
->renderWithHooks
前,没有右侧侧黄色部分
只有调用了renderWithHooks后才开始有右侧黄色部分
无论状态 Hook 或副作用 Hook 都按照调用顺序存储在 fiber.memoizedState 链表中
fiber树更新阶段, 把current.memoizedState链表上的所有Hook按照顺序克隆到workInProgress.memoizedState上, 实现数据的持久化.
注意
其中 hook.queue 与 fiber.updateQueue 虽然都是 update 环形链表, 尽管 update 对象的数据结构与处理方式都高度相似, 但是这 2 个队列中的 update 对象是完全独立的. hook.queue 只作用于 hook 对象的状态维护, 切勿与 fiber.updateQueue 混淆.
为什么hooks不能写在条件判断中?
hook会按顺序存储在链表中,如果写在条件判断中,就没法保持链表的顺序