useState源码分析

useState

eg: const [count, setCount] = React.useState(0);
入口

export function useState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  const dispatcher = resolveDispatcher();
  return dispatcher.useState(initialState);
}
function resolveDispatcher() {
  const dispatcher = ReactCurrentDispatcher.current;
  return dispatcher;
}

useState挂在dispatcher上,resolveDispatcher() 返回的是 ReactCurrentDispatcher.current, ReactCurrentDispatcher 是什么呢?

import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes';

/**
 * Keeps track of the current dispatcher.
 */
const ReactCurrentDispatcher = {
  /**
   * @internal
   * @type {ReactComponent}
   */
  current: (null: null | Dispatcher),
};

React Hooks 的渲染核心是renderWithHooks,在renderWithHooks函数中,

export function renderWithHooks<Props, SecondArg>(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: (p: Props, arg: SecondArg) => any,
  props: Props,
  secondArg: SecondArg,
  nextRenderLanes: Lanes,
): any {
  // ....
  
    ReactCurrentDispatcher.current =
      current === null || current.memoizedState === null
        ? HooksDispatcherOnMount
        : HooksDispatcherOnUpdate;
        
// .....

const HooksDispatcherOnMount: Dispatcher = {
  readContext,
// ...
  useCallback: mountCallback,
  useContext: readContext,
  useEffect: mountEffect,
  useMemo: mountMemo,


  useState: mountState,
  // ...
};

const HooksDispatcherOnUpdate: Dispatcher = {
  readContext,
// ...
  useCallback: updateCallback,
  useContext: readContext,
  useEffect: updateEffect,
  useMemo: updateMemo,
  useRef: updateRef,
  useState: updateState,
 // ....
};
}
  • 当挂载时,ReactCurrentDispatcher.current为HooksDispatcherOnMount,更新时,为HooksDispatcherOnUpdate。在调用useState时,分别调用的不同的函数。挂载时,首先会调用mountWorkInProgressHook函数,生成hooks对象。
    从hooks的生成过程中可以看出:hook其实是以链表的形式存储起来的。每一个hook都有一个指向下一个hook的指针。所以在开发时,hook要按照固定的顺序来,不能在判断语句中使用hook。
function mountWorkInProgressHook() {
  
  // 初始化的hook对象
  var hook = {
    memoizedState: null, // 存储更新后的state值
    baseState: null,  // 存储更新前的state
    baseQueue,
    queue: null,   // UpdateQueue
    next: null
  };

  // workInProgressHook是一个全局变量,表示当前正在处理的hook
  // 如果workInProgressHook链表为null就将新建的hook对象赋值给它,如果不为null,那么就加在链表尾部。
  if (workInProgressHook === null) {
    currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
  } else {
    workInProgressHook = workInProgressHook.next = hook;
  }

  return workInProgressHook;
}

生成hook对象之后,会对hook对象进行初始化。以下是具体流程:

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; // 第二步:获取初始值并初始化hook对象
  const queue = hook.queue = {
	  // 保存 update 对象
	  pending: null,
	  // 保存dispatchAction.bind()的值
	  dispatch: null,
	  // 一次新的dispatch触发前最新的reducer
	  // useState 保存固定函数: 可以理解为一个react 内置的reducer
	  // (state, action) => { return typeof action === 'function' ? action(state) : action }
	  lastRenderedReducer: reducer
	  // 一次新的dispatch触发前最新的state
	  lastRenderedState: (initialState: any),
 }
  // 绑定当前 fiber 和 queue.
  const dispatch: Dispatch<
    BasicStateAction<S>,
  > = (queue.dispatch = (dispatchAction.bind(
    null,
    currentlyRenderingFiber,
    queue,
  ): any));
  // 返回当前状态和修改状态的方法 
  return [hook.memoizedState, dispatch];
}

整个初始化完成之后,setCount又是怎么对count值进行更新的呢?实际上就是通过dispatchAction方法进行更新的。

// currentlyRenderingFiber$1是一个全局变量,表示当前正在渲染的FiberNode
var dispatch = queue.dispatch = dispatchAction.bind(null, currentlyRenderingFiber$1, queue);
function dispatchAction(fiber,queue,action) {
    // ...
    // 1. 创建update对象
    // 该对象保存的是调度优先级/state/reducer以及用户调用dispatch/setState 时传入的action
    const update: Update<S, A> = {  
	    lane,
	    action,
	    eagerReducer: null,
	    eagerState: null,
	    next: (null: any),
	 };
	// 2. 将update更新到queue.pending中,最后的update.next 指向第一个update对象,形成一个闭环。
	const pending = queue.pending;
	  if (pending === null) {
	    // This is the first update. Create a circular list.
	    update.next = update;
	  } else {
	    update.next = pending.next;
	    pending.next = update;
	  }
	  queue.pending = update;
}

可以知道,pending中存储了所有的update,但是state值还没有改变,只是被缓存起来了。state值会在下一次render时,进行更新。

  • 在更新时,updateState实际上调用的是updateReducer函数。
function updateReducer<S, I, A>(
  reducer: (S, A) => S,
  initialArg: I,
  init?: I => S,
): [S, Dispatch<A>] {
 // 获取当前的hook
  const hook = updateWorkInProgressHook();
  const queue = hook.queue;
 
  // 更新 reducer
  queue.lastRenderedReducer = reducer;

  const current: Hook = (currentHook: any);

  // fiber 渲染后,尚未执行完成 update 更新,存于 currentFiber
  let baseQueue = current.baseQueue;

  // 最后一次的update对象
  const pendingQueue = queue.pending;
  if (pendingQueue !== null) {
    // 将未完成update更新插入到链表的头部。
    if (baseQueue !== null) {
      // Merge the pending queue and the base queue.
      const baseFirst = baseQueue.next;
      const pendingFirst = pendingQueue.next;
      baseQueue.next = pendingFirst;
      pendingQueue.next = baseFirst;
    }
    current.baseQueue = baseQueue = pendingQueue;
    queue.pending = null;
  }

  if (baseQueue !== null) {
    // We have a queue to process.
    const first = baseQueue.next;
    let newState = current.baseState;

    let newBaseState = null;
    let newBaseQueueFirst = null;
    let newBaseQueueLast = null;
    let update = first;
    // 调度,获取最新的state
    do {
      const updateLane = update.lane;
      if (!isSubsetOfLanes(renderLanes, updateLane)) {
        const clone: Update<S, A> = {
          lane: updateLane,
          action: update.action,
          eagerReducer: update.eagerReducer,
          eagerState: update.eagerState,
          next: (null: any),
        };
        if (newBaseQueueLast === null) {
          newBaseQueueFirst = newBaseQueueLast = clone;
          newBaseState = newState;
        } else {
          newBaseQueueLast = newBaseQueueLast.next = clone;
        }
        // Update the remaining priority in the queue.
        // TODO: Don't need to accumulate this. Instead, we can remove
        // renderLanes from the original lanes.
        currentlyRenderingFiber.lanes = mergeLanes(
          currentlyRenderingFiber.lanes,
          updateLane,
        );
        markSkippedUpdateLanes(updateLane);
      } else {
        // This update does have sufficient priority.

        if (newBaseQueueLast !== null) {
          const clone: Update<S, A> = {
            // This update is going to be committed so we never want uncommit
            // it. Using NoLane works because 0 is a subset of all bitmasks, so
            // this will never be skipped by the check above.
            lane: NoLane,
            action: update.action,
            eagerReducer: update.eagerReducer,
            eagerState: update.eagerState,
            next: (null: any),
          };
          newBaseQueueLast = newBaseQueueLast.next = clone;
        }

        // 对应 dispatchAction 函数中的 update.eagerReducer 赋值。
        if (update.eagerReducer === reducer) {
          // If this update was processed eagerly, and its reducer matches the
          // current reducer, we can use the eagerly computed state.
          newState = ((update.eagerState: any): S);
        } else {
          // 否则计算最新的state
          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: any);
    }

    // Mark that the fiber performed work, but only if the new state is
    // different from the current state.
    if (!is(newState, hook.memoizedState)) {
      markWorkInProgressReceivedUpdate();
    }

    hook.memoizedState = newState;
    hook.baseState = newBaseState;
    hook.baseQueue = newBaseQueueLast;

    queue.lastRenderedState = newState;
  }

  const dispatch: Dispatch<A> = (queue.dispatch: any);
  return [hook.memoizedState, dispatch];
}

//  updateState做的事情,实际上就是拿到更新队列,循环队列,并根据每一个update对象对当前hook进行状态更新。最后返回最终的结果。
function updateState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  return updateReducer(basicStateReducer, (initialState: any));
}

React通过单链表来存储hooks。在执行useState函数时,每个hook节点通过循环链表存储所有的更新操作,在update阶段会依次执行update循环链表中的所有更新操作,最终拿到最新的state并返回。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值