useState源码解析

205 阅读2分钟

useState是React Hook中最常用的一个函数,它负责在函数组件中管理状态。

useState函数的声明:

复制代码
function useState(initialState) {
  const dispatcher = resolveDispatcher();
  return dispatcher.useState(initialState);
}

useState实际上是通过resolveDispatcher获取了dispatcher对象,并调用了其useState方法。那么,dispatcher对象是个啥呢?

dispatcher是React内部用于处理各种不同类型的Hook函数的对象。在使用每种Hook函数时,都会根据其类型获取相应的dispatcher对象。具体实现方式如下:

复制代码
function resolveDispatcher() {
  const fiber = ReactCurrentOwner.current;
  return fiber?.stateNode?.__reactInternalMemoizedHookDispatcher || defaultDispatcher;
}

先通过ReactCurrentOwner获取当前组件对应的fiber对象。然后,从fiber对象上的stateNode属性中获取了与此组件相关联的Hook Dispatcher对象。如果找不到,则返回默认的dispatcher对象。

dispatcher中的useState方法是咋干活的?

复制代码
export function useState<S>(initialState: (() => S) | S) {
  const [hook, next] = updateWorkInProgressHook();
  if (hook.queue === null) {
    // 如果该hook没有挂载队列,则初始化一个
    const initialStateObject =
      typeof initialState === 'function'
        ? (initialState as (() => S))()
        : initialState;
    hook.memoizedState = hook.queue = {
      // 队列中的第一个节点是当前状态值
      pending: null,
      dispatch: null,
      lastRenderedReducer: basicStateReducer,
      lastRenderedState: (initialStateObject as S),
    };
  }
  const queue = hook.queue;
  const dispatch = queue.dispatch ??= dispatchAction.bind(
    null,
    currentlyRenderingFiber$1,
    queue,
  );

  if (isUpdatingOpaqueValueInRenderPhaseInDEV) {
    // prohibit use of setState inside an updater
    disableLogs();
    try {
      // 当hook正在渲染和执行更新时,如果使用了setState会发出警告
      if (hook.isRendering) {
        console.error(
          'Warning: Cannot update a state variable during rendering. ' +
            'You should only render once.',
        );
      }
      if (queue.isCurrentlyProcessingDispatch) {
        console.error(
          'Warning: Cannot update a state variable while a component is ' +
            'rendering. React ensures that your setState ' +
            'call never happens during the current render. It will become ' +
            'undefined in a future version of React.',
        );
      }
    } finally {
      reenableLogs();
    }
  }

  const memoizedState = hook.memoizedState;
  let update = queue.pending;
  if (update !== null) {
    // 如果有状态更新,则进行状态合并
    do {
      // 更新后的值由多个partialStates组成
      const action = update.action;
      memoizedState = action(memoizedState);
    } while ((update = update.next) !== null);

    queue.pending = null;
  }
  
  // 将状态更新操作封装为setState函数进行返回
  return [
    memoizedState,
    dispatch as Dispatch<SetStateAction<S>>,
  ];
}

在useState方法中,通过updateWorkInProgressHook获取当前组件的hook对象。如果该hook对象还没有挂载过队列,则创建一个新的队列,并将当前状态值保存到队列的第一个节点中。

如果有待处理的状态更新,则进行状态合并,根据多个partialStates计算出最终的新状态,并返回给调用方。

useState函数将状态更新操作封装成一个名为setState的函数,并将其作为第二个返回值返回。当我们调用这个函数时,实际上就是向队列中添加一个新的状态更新节点,等待下一次重新渲染时进行状态合并。

useState的实现方式比较清晰简单,主要涉及到的核心概念包括:Hook、Hook Dispatcher以及状态更新队列等。掌握了这些基础之后,就可以更加灵活地使用useState,并深入理解React Hook的运行原理了。