React Router

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. 只能在函数组件顶层调用
  2. 不能在循环、条件或嵌套函数中调用
  3. 调用顺序必须保持一致

🔍 源码实现分析

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 的实现基于以下几个核心概念:

  1. 链表结构:每个 Hook 都是一个链表节点
  2. 调用顺序:通过调用顺序来识别不同的 Hook
  3. 状态分离:每个 Hook 的状态独立存储
  4. 更新机制:通过更新队列管理状态变化

理解这些原理有助于我们更好地使用 Hooks,避免常见的陷阱,并编写出更高效的 React 组件。


下一步Hooks 深度解析 - 深入分析更多 Hooks 的实现细节