Hooks 基础原理
深入理解 React Hooks 的底层实现原理,包括数据结构、调用机制和状态管理
Hooks 基础原理
Hooks 是 React 16.8 引入的重要特性,它让函数组件也能拥有状态和生命周期。本文将深入分析 Hooks 的底层实现原理。
🎯 核心概念
Hooks 的本质
Hooks 本质上是一个链表结构,每个 Hook 都是一个对象,包含以下关键属性:
type Hook = {
memoizedState: any, // 存储 Hook 的状态
baseQueue: Update | null, // 基础更新队列
baseState: any, // 基础状态
queue: UpdateQueue | null, // 更新队列
next: Hook | null, // 指向下一个 Hook
};Hooks 调用规则
React 通过以下规则确保 Hooks 的正确调用:
- 只能在函数组件顶层调用
- 不能在循环、条件或嵌套函数中调用
- 调用顺序必须保持一致
🔍 源码实现分析
1. Hooks 数据结构
// packages/react-reconciler/src/ReactFiberHooks.js
function createHook(): Hook {
return {
memoizedState: null,
baseQueue: null,
baseState: null,
queue: null,
next: null,
};
}2. 当前 Hook 的获取
// packages/react-reconciler/src/ReactFiberHooks.js
function resolveCurrentlyRenderingFiber(): Fiber {
const current = currentlyRenderingFiber;
if (current === null) {
throw new Error('Invalid hook call. Hooks can only be called inside of the body of a function component.');
}
return current;
}
function getCurrentHook(): Hook | null {
const fiber = resolveCurrentlyRenderingFiber();
let currentHook = fiber.memoizedState;
if (currentHook === null) {
// 这是第一个 Hook
if (fiber.alternate === null) {
// 这是首次渲染
currentHook = fiber.memoizedState = createHook();
} else {
// 这是更新渲染
currentHook = fiber.memoizedState = fiber.alternate.memoizedState;
}
}
return currentHook;
}3. 挂载 Hook
// packages/react-reconciler/src/ReactFiberHooks.js
function mountWorkInProgressHook(): Hook {
const hook: Hook = {
memoizedState: null,
baseQueue: null,
baseState: null,
queue: null,
next: null,
};
if (workInProgressHook === null) {
// 这是第一个 Hook
currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
} else {
// 添加到链表末尾
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}4. 更新 Hook
// packages/react-reconciler/src/ReactFiberHooks.js
function updateWorkInProgressHook(): Hook {
let nextCurrentHook: Hook | null;
if (currentHook === null) {
const current = currentlyRenderingFiber.alternate;
if (current === null) {
throw new Error('Update hook called on initial render');
} else {
nextCurrentHook = current.memoizedState;
}
} else {
nextCurrentHook = currentHook.next;
}
const newHook: Hook = {
memoizedState: nextCurrentHook.memoizedState,
baseQueue: nextCurrentHook.baseQueue,
baseState: nextCurrentHook.baseState,
queue: nextCurrentHook.queue,
next: null,
};
if (workInProgressHook === null) {
currentlyRenderingFiber.memoizedState = workInProgressHook = newHook;
} else {
workInProgressHook = workInProgressHook.next = newHook;
}
currentHook = nextCurrentHook;
return workInProgressHook;
}🎣 核心 Hooks 实现
useState 实现
// packages/react-reconciler/src/ReactFiberHooks.js
function useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<SetStateAction<S>>] {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
function mountState<S>(
initialState: (() => S) | S,
): [S, Dispatch<SetStateAction<S>>] {
const hook = mountWorkInProgressHook();
if (typeof initialState === 'function') {
initialState = initialState();
}
hook.memoizedState = hook.baseState = initialState;
const queue = (hook.queue = {
pending: null,
interleaved: null,
lanes: NoLanes,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: (initialState: any),
});
const dispatch: Dispatch<SetStateAction<S>> = (queue.dispatch = (dispatchSetState.bind(
null,
currentlyRenderingFiber,
queue,
): any));
return [hook.memoizedState, dispatch];
}useEffect 实现
// packages/react-reconciler/src/ReactFiberHooks.js
function useEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
const dispatcher = resolveDispatcher();
return dispatcher.useEffect(create, deps);
}
function mountEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
return mountEffectImpl(
UpdateEffect | PassiveEffect,
HookPassive,
create,
deps,
);
}
function mountEffectImpl(
fiberFlags: Flags,
hookFlags: HookFlags,
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
hook.memoizedState = pushEffect(
HookHasEffect | hookFlags,
create,
undefined,
nextDeps,
);
}🔄 状态更新机制
更新队列
// packages/react-reconciler/src/ReactFiberHooks.js
function dispatchSetState<S, A>(
fiber: Fiber,
queue: UpdateQueue<S, A>,
action: A,
): void {
const lane = requestUpdateLane(fiber);
const update: Update<S, A> = {
lane,
action,
hasEagerState: false,
eagerState: null,
next: (null: any),
};
if (isRenderPhaseUpdate(fiber)) {
// 在渲染阶段,直接更新
enqueueRenderPhaseUpdate(queue, update);
} else {
// 在事件处理中,加入队列
enqueueUpdate(fiber, update, lane);
const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
}
}状态计算
// packages/react-reconciler/src/ReactFiberHooks.js
function getStateFromUpdate<S>(
workInProgress: Fiber,
queue: UpdateQueue<S, any>,
update: Update<S, any>,
prevState: S,
nextProps: any,
instance: any,
): any {
switch (update.tag) {
case ReplaceState: {
const payload = update.payload;
if (typeof payload === 'function') {
return payload.call(instance, prevState, nextProps);
}
return payload;
}
case CaptureUpdate: {
workInProgress.flags = (workInProgress.flags & ~ShouldCapture) | DidCapture;
}
case UpdateState: {
const payload = update.payload;
let partialState;
if (typeof payload === 'function') {
partialState = payload.call(instance, prevState, nextProps);
} else {
partialState = payload;
}
if (partialState === null || partialState === undefined) {
return prevState;
}
return assign({}, prevState, partialState);
}
case ForceUpdate: {
hasForceUpdate = true;
return prevState;
}
}
return prevState;
}🎯 实际应用示例
自定义 Hook 实现
// 自定义 useCounter Hook
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
const increment = useCallback(() => {
setCount(prev => prev + 1);
}, []);
const decrement = useCallback(() => {
setCount(prev => prev - 1);
}, []);
const reset = useCallback(() => {
setCount(initialValue);
}, [initialValue]);
return { count, increment, decrement, reset };
}性能优化示例
// 使用 useMemo 优化计算
function ExpensiveComponent({ data }) {
const processedData = useMemo(() => {
return data.map(item => ({
...item,
processed: heavyComputation(item)
}));
}, [data]);
return <div>{/* 渲染处理后的数据 */}</div>;
}
// 使用 useCallback 优化回调
function ParentComponent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(prev => prev + 1);
}, []);
return <ChildComponent onClick={handleClick} />;
}🚀 最佳实践
1. Hooks 调用顺序
// ✅ 正确:在顶层调用
function MyComponent() {
const [state, setState] = useState(0);
const effect = useEffect(() => {}, []);
if (condition) {
// 错误:不能在条件中调用
// const [state2, setState2] = useState(0);
}
return <div>{state}</div>;
}2. 依赖项管理
// ✅ 正确:包含所有依赖
function MyComponent({ id, data }) {
useEffect(() => {
fetchData(id, data);
}, [id, data]); // 包含所有依赖
// ❌ 错误:缺少依赖
useEffect(() => {
fetchData(id, data);
}, []); // 缺少 id 和 data
}3. 清理函数
// ✅ 正确:使用清理函数
function MyComponent() {
useEffect(() => {
const subscription = subscribe();
return () => {
subscription.unsubscribe();
};
}, []);
}📝 总结
Hooks 的实现基于以下几个核心概念:
- 链表结构:每个 Hook 都是一个链表节点
- 调用顺序:通过调用顺序来识别不同的 Hook
- 状态分离:每个 Hook 的状态独立存储
- 更新机制:通过更新队列管理状态变化
理解这些原理有助于我们更好地使用 Hooks,避免常见的陷阱,并编写出更高效的 React 组件。
下一步:Hooks 深度解析 - 深入分析更多 Hooks 的实现细节