useState源码阅读,如果useState第二个返回值传入一个函数会发生什么?

在看开源项目源码过程中发现了这样一段代码
setState是useState返回数组的第二个参数,可以传一个函数,react官方文档没有记录这样的用法,决定带着问题去看看useState源码

setState(value => {
  const newValue = alignInRange(value + offset);
  return newValue;
});
复制代码

首先看useState代码

export function useState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  if (__DEV__) {
    currentHookNameInDev = 'useState';
  }
  return useReducer(
    basicStateReducer,
    // useReducer has a special case to support lazy useState initializers
    (initialState: any),
  );
}
复制代码

可见useState基于useReducer,basicStateReducer传入了useReducer的第一个参数
再看basicStateReducer代码

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

回顾一下useReducer的用法

const [state, dispatch] = useReducer(reducer, initialState);
复制代码

dispatch是否可以用一个函数做入参呢?
看看useReducer最后一行,返回了

const dispatch = (queue.dispatch = (dispatchAction.bind(
      null,
      currentlyRenderingComponent,
      queue,
    )));
return [workInProgressHook.memoizedState, dispatch];
复制代码

对应了useReducer返回的state和dispatch
dispatch是dispatchAction的拷贝,this指向useReducer并具有初始参数currentlyRenderingComponent和queue
再来看看dispatchAction传入一个函数会发生什么?

function dispatchAction<A>(
  componentIdentity: Object,
  queue: UpdateQueue<A>,
  action: A,   // 把action函数传入...
){
    didScheduleRenderPhaseUpdate = true;
    const update: Update<A> = {
      action,    // action放在update对象中
      next: null,
    };
    if (renderPhaseUpdates === null) {   // renderPhaseUpdates看名字猜测是更新阶段的产生的更新
    
      renderPhaseUpdates = new Map();
    }
    const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);  // 如果这个更新队列的不存在这个值,则是第一次更新
    if (firstRenderPhaseUpdate === undefined) {  
      renderPhaseUpdates.set(queue, update);
    } else {
      // Append the update to the end of the list.
      let lastRenderPhaseUpdate = firstRenderPhaseUpdate;
      while (lastRenderPhaseUpdate.next !== null) {
        lastRenderPhaseUpdate = lastRenderPhaseUpdate.next;  // 找到最后一次更新
      }
      lastRenderPhaseUpdate.next = update;   // 修改最后一次更新的next指向,指向这一次需要更新的
    }
}
复制代码

所以dispatchAction函数的作用是把需要更新的内容action添加到需要更新的action的链表末尾
所以调用dispatch后会创建一个update,在所有update收集完后会调度一次React更新
更新时会执行我们的function component,就会再次执行useState,进而执行useReducer
在来看看useReducer代码(去掉了不关键代码后)

export function useReducer<S, I, A>(
  reducer: (S, A) => S,
  initialArg: I,
  init?: I => S,
): [S, Dispatch<A>] {
  currentlyRenderingComponent = resolveCurrentlyRenderingComponent();
  workInProgressHook = createWorkInProgressHook();   // 返回了Hook,包含memoizedState,queue,next属性,next指向了下一个将要被指向的hook
    // This is a re-render. Apply the new render phase updates to the previous
    // current hook.
    const queue: UpdateQueue<A> = (workInProgressHook.queue: any);
    const dispatch: Dispatch<A> = (queue.dispatch: any);
    if (renderPhaseUpdates !== null) {
      // Render phase updates are stored in a map of queue -> linked list
      const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
      if (firstRenderPhaseUpdate !== undefined) {
        renderPhaseUpdates.delete(queue);
        let newState = workInProgressHook.memoizedState;
        let update = firstRenderPhaseUpdate;
        do {
          // Process this render phase update. We don't have to check the
          // priority because it will always be the same as the current
          // render's.
          const action = update.action;
          newState = reducer(newState, action);
          update = update.next;
        } while (update !== null);

        workInProgressHook.memoizedState = newState;

        return [newState, dispatch];
      }
    }
    return [workInProgressHook.memoizedState, dispatch];
}
复制代码

执行useReducer后我们拿到了Hook对象,里面的queue属性保存了有哪些需要更新,依次进行更新
上面10-31行就是执行更新的代码
其中23行的action是我们调用dispatch的入参,reducer是在调用useState时传入useReducer的
也就是上面提到的,basicStateReducer

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

所以我们action如果传入一个函数,就会被执行,并且入参是更新前的state
找到答案了!

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享