react中的useState源码(初步学习探索)

387 阅读4分钟

当组件装载的时候

function mountState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  const hook = mountWorkInProgressHook();
  if (typeof initialState === 'function') {
    // $FlowFixMe: Flow doesn't like mixed types
    initialState = initialState();
  }
  hook.memoizedState = hook.baseState = initialState;
  const queue = (hook.queue = {
    pending: null,
    dispatch: null,
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: (initialState: any),
  });
  const dispatch: Dispatch<
    BasicStateAction<S>,
  > = (queue.dispatch = (dispatchAction.bind(
    null,
    currentlyRenderingFiber,
    queue,
  ): any));
  return [hook.memoizedState, dispatch];
}

可以看出代码主要就是初始化hook.memoizedStatequeue.dispatch的值,其中hook.queue中包含lastRenderedReducerlastRenderedState,根据字面意思我们就知道,保存的是当前的值,其中lastRenderedReducer的值为:

function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
  // $FlowFixMe: Flow doesn't like mixed types
  return typeof action === 'function' ? action(state) : action;
}

lastRenderedState的值为:initialState。也就是装载时的值。 其中dispatch也就是更新state的。

function dispatchAction<S, A>(
  fiber: Fiber,
  queue: UpdateQueue<S, A>,
  action: A,
) {
  
  const eventTime = requestEventTime();
  const suspenseConfig = requestCurrentSuspenseConfig();
  const lane = requestUpdateLane(fiber, suspenseConfig);

  const update: Update<S, A> = {
    eventTime,
    lane,
    suspenseConfig,
    action,
    eagerReducer: null,
    eagerState: null,
    next: (null: any),
  };

  const pending = queue.pending;
  if (pending === null) {
    update.next = update;
  } else {
    update.next = pending.next;
    pending.next = update;
  }
  queue.pending = update;

  const alternate = fiber.alternate;
  if (
    fiber === currentlyRenderingFiber ||
    (alternate !== null && alternate === currentlyRenderingFiber)
  ) {
    didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate = true;
  } else {
    if (
      fiber.lanes === NoLanes &&
      (alternate === null || alternate.lanes === NoLanes)
    ) {
      const lastRenderedReducer = queue.lastRenderedReducer;
      if (lastRenderedReducer !== null) {
        const currentState: S = (queue.lastRenderedState: any);
        const eagerState = lastRenderedReducer(currentState, action);
        update.eagerReducer = lastRenderedReducer;
        update.eagerState = eagerState;
        if (is(eagerState, currentState)) {
          return;
        }
      }
    }
    scheduleUpdateOnFiber(fiber, lane, eventTime);
  }
}

我删除了没有必要的注释和调试代码。我们知道装载的时候传进来的参数,其中:

  • filter
currentlyRenderingFiber
  • queue
{
  pending: null,
  dispatch: null,
  lastRenderedReducer: basicStateReducer,
  lastRenderedState: (initialState: any),
}
  • action 这是使用者传入的

说个简单例子来看:

// 初始的状态
const initialState = 0
// state定义
const [count, setCount] = useState(initialState)
// 当我们更新的时候
setCount((count) => count + 1)
// 相当于调用了
dispatch((count) => count + 1) // 这里的action就是(count) => count + 1

dispatchAction函数中下面三个:

const eventTime = requestEventTime();
const suspenseConfig = requestCurrentSuspenseConfig();
const lane = requestUpdateLane(fiber, suspenseConfig);

跟请求相关,这个跟内部调度有关,目前还不知道。暂时先放一放。

准备功能,只不过这些都不是这次关心的重点,或者以后有用:

const pending = queue.pending;
if (pending === null) {
  update.next = update;
} else {
  update.next = pending.next;
  pending.next = update;
}
queue.pending = update;

肯定会走if,因为装载的时候pending的值为null,所以这里相当于:

update.next = update;
queue.pending = update;

看值是怎么更新的:

const currentState: S = (queue.lastRenderedState: any);
const eagerState = lastRenderedReducer(currentState, action);
update.eagerReducer = lastRenderedReducer;
update.eagerState = eagerState;

可以看出,更新的值是通过lastRenderedReducer得到的。这个函数的定义在上面,非常简单。这些准备工作做完了,就开始了react内部的一些功能,这块以后再看。

scheduleUpdateOnFiber(fiber, lane, eventTime);

当组件更新的时候

function updateState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  return updateReducer(basicStateReducer, (initialState: any));
}

这个函数会在组件除了初次渲染以后每次渲染的时候调用。我们看看updateReducer函数:

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: Hook = (currentHook: any);
  let baseQueue = current.baseQueue;
  const pendingQueue = queue.pending;
  if (pendingQueue !== null) {
    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 suspenseConfig = update.suspenseConfig;
      const updateLane = update.lane;
      const updateEventTime = update.eventTime;
      if (!isSubsetOfLanes(renderLanes, updateLane)) {
        const clone: Update<S, A> = {
          eventTime: updateEventTime,
          lane: updateLane,
          suspenseConfig: suspenseConfig,
          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;
        }
        currentlyRenderingFiber.lanes = mergeLanes(
          currentlyRenderingFiber.lanes,
          updateLane,
        );
        markSkippedUpdateLanes(updateLane);
      } else {
        if (newBaseQueueLast !== null) {
          const clone: Update<S, A> = {
            eventTime: updateEventTime,
            lane: NoLane,
            suspenseConfig: update.suspenseConfig,
            action: update.action,
            eagerReducer: update.eagerReducer,
            eagerState: update.eagerState,
            next: (null: any),
          };
          newBaseQueueLast = newBaseQueueLast.next = clone;
        }
        markRenderEventTimeAndConfig(updateEventTime, suspenseConfig);

        if (update.eagerReducer === reducer) {
          newState = ((update.eagerState: any): S);
        } 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: any);
    }
    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];
}

从返回值看看这个函数都做了什么处理。看看返回值跟上面的一样[hook.memoizedState, dispatch],跟上面的都是一样的,其中第一个hook.memoizedState的相关的:

let newState = current.baseState;
do {
  const suspenseConfig = update.suspenseConfig;
  const updateLane = update.lane;
  const updateEventTime = update.eventTime;
  if (!isSubsetOfLanes(renderLanes, updateLane)) {
    const clone: Update<S, A> = {
      eventTime: updateEventTime,
      lane: updateLane,
      suspenseConfig: suspenseConfig,
      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;
    }
    currentlyRenderingFiber.lanes = mergeLanes(
      currentlyRenderingFiber.lanes,
      updateLane,
    );
    markSkippedUpdateLanes(updateLane);
  } else {
    if (newBaseQueueLast !== null) {
      const clone: Update<S, A> = {
        eventTime: updateEventTime,
        lane: NoLane,
        suspenseConfig: update.suspenseConfig,
        action: update.action,
        eagerReducer: update.eagerReducer,
        eagerState: update.eagerState,
        next: (null: any),
      };
      newBaseQueueLast = newBaseQueueLast.next = clone;
    }
    markRenderEventTimeAndConfig(updateEventTime, suspenseConfig);

    if (update.eagerReducer === reducer) {
      newState = ((update.eagerState: any): S);
    } 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: any);
}
if (!is(newState, hook.memoizedState)) {
  markWorkInProgressReceivedUpdate();
}
hook.memoizedState = newState;

同时也发现只有当baseQueue不为null的时候才会进行,否则使用原来的值,所以还需要看一下baseQueue的来源:

let baseQueue = current.baseQueue;
const pendingQueue = queue.pending;
if (pendingQueue !== null) {
  if (baseQueue !== null) {
    const baseFirst = baseQueue.next;
    const pendingFirst = pendingQueue.next;
    baseQueue.next = pendingFirst;
    pendingQueue.next = baseFirst;
  }
  current.baseQueue = baseQueue = pendingQueue;
  queue.pending = null;
}

看了看baseQueue的来源发现这一块目前还不能了解。

所以关于这个我们假定不为null,也就是if (baseQueue !== null) 这个条件满足。

发现大部分看不懂,其中:

if (update.eagerReducer === reducer) {
  newState = ((update.eagerState: any): S);
} else {
  const action = update.action;
  newState = reducer(newState, action);
}

其中的update.eagerReducer我们可以知道,在装载的时候这里的值,也就是初始化的lastRenderedReducer,这个的值默认是下面的:

function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
  // $FlowFixMe: Flow doesn't like mixed types
  return typeof action === 'function' ? action(state) : action;
}

也就是当以前的和现在的相同则使用以前的state,否者就需要调用当前的更新state,只不过我并不知道什么情况下相等,什么情况下不想等。这一点需要我进一步学习才可得知。

我也是在学习怎么看源码,所以如果真的有人看到了,并发现不对的地方,还望指出,看源码的方法如果有更好也可以告知。在此感谢