Appearance
Hooks链表结构详解 - React Hooks底层数据结构
1. Hooks链表概述
1.1 为什么使用链表
typescript
const hooksLinkedListReason = {
设计需求: {
多个Hooks: '一个组件可以使用多个Hooks',
顺序固定: 'Hooks调用顺序必须一致',
状态持久: '状态需要在渲染间保持',
快速访问: '按顺序快速访问每个Hook'
},
为什么不用数组: {
问题: '条件调用会导致索引错位',
示例: `
function Component() {
const [a, setA] = useState(0);
if (someCondition) {
const [b, setB] = useState(0); // 索引会变化!
}
const [c, setC] = useState(0);
}
// 第一次: a=index[0], b=index[1], c=index[2]
// 第二次: a=index[0], c=index[1] (错位!)
`
},
链表优势: [
'固定顺序',
'不受条件影响',
'快速遍历',
'便于添加和删除'
]
};1.2 Hook数据结构
typescript
// Hook节点结构
type Hook = {
// 当前状态值
memoizedState: any;
// 基础状态(用于useReducer)
baseState: any;
// 基础更新队列
baseQueue: Update<any, any> | null;
// 待处理更新队列
queue: UpdateQueue<any, any> | null;
// 指向下一个Hook
next: Hook | null;
};
// 更新队列
type UpdateQueue<S, A> = {
// 待处理的更新
pending: Update<S, A> | null;
// 调度优先级
lanes: Lanes;
// dispatch函数
dispatch: ((A) => void) | null;
// 最后一个渲染的reducer
lastRenderedReducer: ((S, A) => S) | null;
// 最后一个渲染的state
lastRenderedState: S | null;
};
// 更新对象
type Update<S, A> = {
// 优先级
lane: Lane;
// 更新action
action: A;
// 是否有急切更新
hasEagerState: boolean;
// 急切状态
eagerState: S | null;
// 指向下一个更新(环形链表)
next: Update<S, A>;
};1.3 Fiber与Hooks的关系
typescript
// Fiber节点中的Hooks
interface Fiber {
// ... 其他属性
// Hooks链表头指针
memoizedState: Hook | null;
// 更新队列
updateQueue: UpdateQueue<any, any> | null;
// ... 其他属性
}
// Hooks存储在Fiber上
const fiberWithHooks = {
tag: FunctionComponent,
type: MyComponent,
stateNode: null,
// Hooks链表
memoizedState: {
memoizedState: 0, // useState的值
next: {
memoizedState: () => {}, // useEffect的清理函数
next: {
memoizedState: {}, // useContext的值
next: null
}
}
}
};2. Hooks链表构建
2.1 mount阶段
typescript
// 当前正在工作的Fiber
let currentlyRenderingFiber: Fiber | null = null;
// Hooks链表
let workInProgressHook: Hook | null = null;
// 首次渲染时挂载Hook
function mountWorkInProgressHook(): Hook {
// 创建新Hook
const hook: 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;
}
// 示例: 三个useState调用
function Component() {
const [count, setCount] = useState(0); // Hook 1
const [name, setName] = useState(''); // Hook 2
const [flag, setFlag] = useState(false); // Hook 3
// Fiber.memoizedState:
// Hook1 -> Hook2 -> Hook3 -> null
}2.2 update阶段
typescript
// 当前Hook在旧Fiber上的指针
let currentHook: Hook | null = null;
// 更新时复用Hook
function updateWorkInProgressHook(): Hook {
let nextCurrentHook: Hook | null;
if (currentHook === null) {
// 第一个Hook
const current = currentlyRenderingFiber.alternate;
nextCurrentHook = current ? current.memoizedState : null;
} else {
// 后续Hook
nextCurrentHook = currentHook.next;
}
let nextWorkInProgressHook: Hook | null;
if (workInProgressHook === null) {
// 第一个Hook
nextWorkInProgressHook = currentlyRenderingFiber.memoizedState;
} else {
// 后续Hook
nextWorkInProgressHook = workInProgressHook.next;
}
if (nextWorkInProgressHook !== null) {
// 已有workInProgress Hook(重复渲染)
workInProgressHook = nextWorkInProgressHook;
nextWorkInProgressHook = workInProgressHook.next;
currentHook = nextCurrentHook;
} else {
// 克隆current Hook
currentHook = nextCurrentHook;
const newHook: Hook = {
memoizedState: currentHook.memoizedState,
baseState: currentHook.baseState,
baseQueue: currentHook.baseQueue,
queue: currentHook.queue,
next: null
};
if (workInProgressHook === null) {
currentlyRenderingFiber.memoizedState = workInProgressHook = newHook;
} else {
workInProgressHook = workInProgressHook.next = newHook;
}
}
return workInProgressHook;
}2.3 Hooks调用流程
typescript
// renderWithHooks入口
function renderWithHooks<Props, SecondArg>(
current: Fiber | null,
workInProgress: Fiber,
Component: (p: Props, arg: SecondArg) => any,
props: Props,
secondArg: SecondArg,
nextRenderLanes: Lanes
): any {
// 设置当前渲染的Fiber
currentlyRenderingFiber = workInProgress;
// 清空Hooks链表
workInProgress.memoizedState = null;
workInProgress.updateQueue = null;
// 根据mount/update选择不同的Hooks实现
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
// 执行函数组件
let children = Component(props, secondArg);
// 清理
ReactCurrentDispatcher.current = ContextOnlyDispatcher;
currentlyRenderingFiber = null;
currentHook = null;
workInProgressHook = null;
return children;
}
// Mount时的Hooks实现
const HooksDispatcherOnMount = {
useState: mountState,
useEffect: mountEffect,
useContext: readContext,
useReducer: mountReducer,
useCallback: mountCallback,
useMemo: mountMemo,
useRef: mountRef
// ... 其他Hooks
};
// Update时的Hooks实现
const HooksDispatcherOnUpdate = {
useState: updateState,
useEffect: updateEffect,
useContext: readContext,
useReducer: updateReducer,
useCallback: updateCallback,
useMemo: updateMemo,
useRef: updateRef
// ... 其他Hooks
};3. 不同Hook的链表应用
3.1 useState链表
typescript
// useState mount实现
function mountState<S>(initialState: (() => S) | S): [S, Dispatch<BasicStateAction<S>>] {
// 创建Hook节点
const hook = mountWorkInProgressHook();
// 初始化状态
if (typeof initialState === 'function') {
initialState = initialState();
}
hook.memoizedState = hook.baseState = initialState;
// 创建更新队列
const queue: UpdateQueue<S, BasicStateAction<S>> = {
pending: null,
lanes: NoLanes,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: initialState
};
hook.queue = queue;
// 创建dispatch函数
const dispatch: Dispatch<BasicStateAction<S>> =
dispatchSetState.bind(null, currentlyRenderingFiber, queue);
queue.dispatch = dispatch;
return [hook.memoizedState, dispatch];
}
// useState update实现
function updateState<S>(initialState: (() => S) | S): [S, Dispatch<BasicStateAction<S>>] {
return updateReducer(basicStateReducer, initialState);
}
// 基础reducer
function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
return typeof action === 'function' ? action(state) : action;
}
// 链表示例
const useStateExample = `
function Component() {
const [count, setCount] = useState(0);
const [name, setName] = useState('');
// Fiber.memoizedState:
// {
// memoizedState: 0,
// queue: { dispatch: setCount, ... },
// next: {
// memoizedState: '',
// queue: { dispatch: setName, ... },
// next: null
// }
// }
}
`;3.2 useEffect链表
typescript
// Effect Hook结构
type Effect = {
tag: HookFlags;
create: () => (() => void) | void;
destroy: (() => void) | void;
deps: Array<any> | null;
next: Effect;
};
// useEffect mount实现
function mountEffect(
create: () => (() => void) | void,
deps: Array<any> | void | null
): void {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
// 标记副作用
currentlyRenderingFiber.flags |= PassiveEffect;
// 创建Effect对象
hook.memoizedState = pushEffect(
HookHasEffect | HookPassive,
create,
undefined,
nextDeps
);
}
// pushEffect添加到环形链表
function pushEffect(
tag: HookFlags,
create: () => (() => void) | void,
destroy: (() => void) | void | undefined,
deps: Array<any> | null
): Effect {
const effect: Effect = {
tag,
create,
destroy,
deps,
next: null as any
};
let componentUpdateQueue: FunctionComponentUpdateQueue | null =
currentlyRenderingFiber.updateQueue as any;
if (componentUpdateQueue === null) {
// 创建新队列
componentUpdateQueue = createFunctionComponentUpdateQueue();
currentlyRenderingFiber.updateQueue = componentUpdateQueue as any;
// 创建环形链表
componentUpdateQueue.lastEffect = effect.next = effect;
} else {
// 添加到环形链表
const lastEffect = componentUpdateQueue.lastEffect;
if (lastEffect === null) {
componentUpdateQueue.lastEffect = effect.next = effect;
} else {
const firstEffect = lastEffect.next;
lastEffect.next = effect;
effect.next = firstEffect;
componentUpdateQueue.lastEffect = effect;
}
}
return effect;
}
// useEffect链表示例
const useEffectExample = `
function Component() {
useEffect(() => {
console.log('Effect 1');
}, []);
useEffect(() => {
console.log('Effect 2');
}, [count]);
// updateQueue.lastEffect: (环形链表)
// Effect2 -> Effect1 -> Effect2 -> ...
// ↑ |
// |______________________|
}
`;3.3 useReducer链表
typescript
// useReducer mount实现
function mountReducer<S, I, A>(
reducer: (S, A) => S,
initialArg: I,
init?: I => S
): [S, Dispatch<A>] {
const hook = mountWorkInProgressHook();
let initialState;
if (init !== undefined) {
initialState = init(initialArg);
} else {
initialState = initialArg as any;
}
hook.memoizedState = hook.baseState = initialState;
const queue: UpdateQueue<S, A> = {
pending: null,
lanes: NoLanes,
dispatch: null,
lastRenderedReducer: reducer,
lastRenderedState: initialState
};
hook.queue = queue;
const dispatch: Dispatch<A> =
dispatchReducerAction.bind(null, currentlyRenderingFiber, queue);
queue.dispatch = dispatch;
return [hook.memoizedState, dispatch];
}
// useReducer update实现
function updateReducer<S, I, A>(
reducer: (S, A) => S,
initialArg: I,
init?: I => S
): [S, Dispatch<A>] {
const hook = updateWorkInProgressHook();
const queue = hook.queue;
queue.lastRenderedReducer = reducer;
const current = currentHook;
let baseQueue = current.baseQueue;
// 处理pending更新
const pendingQueue = queue.pending;
if (pendingQueue !== null) {
// 合并pending到base
if (baseQueue !== null) {
const baseFirst = baseQueue.next;
const pendingFirst = pendingQueue.next;
baseQueue.next = pendingFirst;
pendingQueue.next = baseFirst;
}
current.baseQueue = baseQueue = pendingQueue;
queue.pending = null;
}
if (baseQueue !== null) {
// 处理更新队列
const first = baseQueue.next;
let newState = current.baseState;
let newBaseState = null;
let newBaseQueueFirst = null;
let newBaseQueueLast = null;
let update = first;
do {
const updateLane = update.lane;
if (!isSubsetOfLanes(renderLanes, updateLane)) {
// 优先级不够,跳过
const clone: Update<S, A> = {
lane: updateLane,
action: update.action,
hasEagerState: update.hasEagerState,
eagerState: update.eagerState,
next: null as any
};
if (newBaseQueueLast === null) {
newBaseQueueFirst = newBaseQueueLast = clone;
newBaseState = newState;
} else {
newBaseQueueLast = newBaseQueueLast.next = clone;
}
currentlyRenderingFiber.lanes = mergeLanes(
currentlyRenderingFiber.lanes,
updateLane
);
markSkippedUpdateLanes(updateLane);
} else {
// 处理更新
if (newBaseQueueLast !== null) {
const clone: Update<S, A> = {
lane: NoLane,
action: update.action,
hasEagerState: update.hasEagerState,
eagerState: update.eagerState,
next: null as any
};
newBaseQueueLast = newBaseQueueLast.next = clone;
}
if (update.hasEagerState) {
newState = update.eagerState;
} else {
const action = update.action;
newState = reducer(newState, action);
}
}
update = update.next;
} while (update !== null && update !== first);
if (newBaseQueueLast === null) {
newBaseState = newState;
} else {
newBaseQueueLast.next = newBaseQueueFirst;
}
hook.memoizedState = newState;
hook.baseState = newBaseState;
hook.baseQueue = newBaseQueueLast;
queue.lastRenderedState = newState;
}
const dispatch = queue.dispatch;
return [hook.memoizedState, dispatch];
}3.4 useMemo和useCallback链表
typescript
// useMemo mount实现
function mountMemo<T>(
nextCreate: () => T,
deps: Array<any> | void | null
): T {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
// useMemo update实现
function updateMemo<T>(
nextCreate: () => T,
deps: Array<any> | void | null
): T {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (prevState !== null) {
if (nextDeps !== null) {
const prevDeps = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
// 依赖未变,返回缓存值
return prevState[0];
}
}
}
// 重新计算
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
// useCallback mount实现
function mountCallback<T>(callback: T, deps: Array<any> | void | null): T {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
hook.memoizedState = [callback, nextDeps];
return callback;
}
// useCallback update实现
function updateCallback<T>(callback: T, deps: Array<any> | void | null): T {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (prevState !== null) {
if (nextDeps !== null) {
const prevDeps = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0];
}
}
}
hook.memoizedState = [callback, nextDeps];
return callback;
}4. Hooks规则的底层原因
4.1 为什么不能条件调用
typescript
// 错误示例
const conditionalHooksError = {
代码: `
function Component({ condition }) {
const [a, setA] = useState(0);
if (condition) {
const [b, setB] = useState(0); // ❌ 错误!
}
const [c, setC] = useState(0);
}
`,
问题: `
第一次渲染(condition=true):
Hook链表: Hook_a -> Hook_b -> Hook_c
第二次渲染(condition=false):
Hook链表期望: Hook_a -> Hook_b -> Hook_c
实际执行: Hook_a -> Hook_c
结果: Hook_c复用了Hook_b的状态!
`,
原因: `
Hooks依赖固定的调用顺序来匹配
条件调用会导致顺序不一致
链表指针错位
`
};4.2 为什么不能在循环中调用
typescript
// 错误示例
const loopHooksError = {
代码: `
function Component({ items }) {
items.forEach(item => {
const [value, setValue] = useState(item); // ❌ 错误!
});
}
`,
问题: `
第一次渲染(items=[1, 2, 3]):
Hook链表: Hook1 -> Hook2 -> Hook3
第二次渲染(items=[1, 2]):
Hook链表期望: Hook1 -> Hook2 -> Hook3
实际执行: Hook1 -> Hook2
结果: Hook3变成孤儿节点
`,
正确做法: `
function Component({ items }) {
// 用一个Hook管理所有状态
const [values, setValues] = useState(items);
// 或者将Hook移到子组件
return items.map(item => <Item key={item.id} item={item} />);
}
function Item({ item }) {
const [value, setValue] = useState(item);
// ...
}
`
};4.3 为什么只能在顶层调用
typescript
const topLevelOnlyReason = {
原因: `
Hooks必须按照固定顺序调用
嵌套函数/回调中调用无法保证顺序
`,
错误示例: `
function Component() {
const handleClick = () => {
const [value, setValue] = useState(0); // ❌ 错误!
};
useEffect(() => {
const [data, setData] = useState([]); // ❌ 错误!
}, []);
}
`,
正确做法: `
function Component() {
const [value, setValue] = useState(0); // ✓ 正确
const [data, setData] = useState([]); // ✓ 正确
const handleClick = () => {
setValue(v => v + 1); // 使用dispatch
};
useEffect(() => {
setData([1, 2, 3]); // 使用dispatch
}, []);
}
`
};5. 更新队列处理
5.1 环形链表结构
typescript
// 更新环形链表
type UpdateQueue<S, A> = {
pending: Update<S, A> | null; // 指向最后一个更新
// ...
};
type Update<S, A> = {
action: A;
next: Update<S, A>; // 指向下一个更新
};
// 添加更新
function dispatchSetState<S, A>(
fiber: Fiber,
queue: UpdateQueue<S, A>,
action: A
): void {
const update: Update<S, A> = {
lane: requestUpdateLane(fiber),
action,
hasEagerState: false,
eagerState: null,
next: null as any
};
// 添加到环形链表
const pending = queue.pending;
if (pending === null) {
// 第一个更新,指向自己
update.next = update;
} else {
// 插入到环的末尾
update.next = pending.next;
pending.next = update;
}
queue.pending = update;
// 调度更新
scheduleUpdateOnFiber(fiber, lane, eventTime);
}
// 处理更新队列
function processUpdateQueue() {
const pending = queue.pending;
if (pending !== null) {
const first = pending.next; // 第一个更新
const last = pending; // 最后一个更新
let update = first;
do {
// 处理更新
newState = reducer(newState, update.action);
update = update.next;
} while (update !== first);
}
}5.2 优先级跳过
typescript
// 处理优先级
function processUpdatesWithPriority() {
let update = first;
let newState = baseState;
let newBaseState = null;
let newBaseQueue = null;
do {
const updateLane = update.lane;
if (!isSubsetOfLanes(renderLanes, updateLane)) {
// 优先级不够,跳过此更新
const clone = {
lane: updateLane,
action: update.action,
next: null
};
// 添加到新的baseQueue
if (newBaseQueue === null) {
newBaseQueue = clone;
newBaseState = newState;
} else {
newBaseQueue.next = clone;
}
} else {
// 处理更新
newState = reducer(newState, update.action);
}
update = update.next;
} while (update !== null);
// 保存跳过的更新
hook.baseState = newBaseState;
hook.baseQueue = newBaseQueue;
hook.memoizedState = newState;
}6. 面试高频问题
typescript
const hooksLinkedListQA = {
Q1: {
question: 'Hooks为什么使用链表而不是数组?',
answer: [
'1. 调用顺序固定',
'2. 不受条件影响',
'3. 快速遍历',
'4. 便于添加删除',
'5. 不会因索引错位导致状态混乱'
]
},
Q2: {
question: 'Hooks链表如何存储?',
answer: `
存储在Fiber节点上:
- Fiber.memoizedState: Hook链表头指针
- 每个Hook通过next指针连接
- Hook.memoizedState: 存储该Hook的状态
- Hook.queue: 存储更新队列
`
},
Q3: {
question: 'Hooks为什么不能条件调用?',
answer: `
Hooks依赖固定的调用顺序:
1. mount时按顺序创建链表
2. update时按顺序遍历链表
3. 条件调用会导致顺序不一致
4. 链表指针错位,状态混乱
`
},
Q4: {
question: 'useState的更新队列如何组织?',
answer: [
'1. 环形链表结构',
'2. queue.pending指向最后一个更新',
'3. 最后一个更新.next指向第一个更新',
'4. 方便从任意位置遍历',
'5. 支持优先级跳过'
]
},
Q5: {
question: 'mount和update时Hooks实现有什么区别?',
answer: `
mount:
- 创建新的Hook节点
- 初始化状态
- 创建dispatch函数
update:
- 复用旧的Hook节点
- 处理更新队列
- 计算新状态
- 保持dispatch引用不变
`
},
Q6: {
question: 'useEffect的链表结构?',
answer: `
两个层次:
1. Hook链表: 存储在Fiber.memoizedState
2. Effect链表: 存储在Fiber.updateQueue
Effect链表是环形链表:
- 方便遍历所有effects
- lastEffect指向最后一个effect
- 从lastEffect.next开始遍历
`
}
};7. 总结
Hooks链表结构的核心要点:
- 数据结构: 单向链表存储Hooks
- 存储位置: Fiber.memoizedState
- 构建时机: 组件渲染时
- 遍历方式: 按顺序访问
- 更新队列: 环形链表
- 规则原因: 保证顺序一致
- 优先级: 支持跳过低优先级更新
理解Hooks链表是掌握Hooks原理的关键。