ReactHooks源码解析之useState及为什么useState要按顺序执行

2020-05-17 22:56:31 浏览数 (1)

前言

从本篇开始,我们讲 React-Hooks 最常用的几个函数,先通过例子来看下React.useState()

代码语言:javascript复制
import React, {useEffect} from 'react';
import React from 'react';

function App() {
  debugger
  const [name, setName] = React.useState( 'chen');

  return (
      <div onClick={()=>setName('jin')}>
        {name}
      </div>
  );
}

export default App;

当执行App()时,会调用React.useState('chen'),因为是useState()的第一次调用,所以此时就会执行源码里的mountState()

一、mountState()

作用:

初始化useState(),并返回一个数组

源码:
代码语言:javascript复制
//第一次更新 state 走这里
//useState的核心源码
//initialState 就是 React.useState(initialState) 设的初始值
function mountState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  const hook = mountWorkInProgressHook();
  //如果 initValue 是 function 的话,则获取执行的结果
  if (typeof initialState === 'function') {
    initialState = initialState();
  }
  hook.memoizedState = hook.baseState = initialState;
  //注意下这边的语法,连等赋值,等同于:
  //hook.queue = { xxx }
  //const queue = hook.queue
  const queue = (hook.queue = {
    last: null,
    dispatch: null,
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: (initialState: any),
  });
  //这边又是一个连等赋值
  const dispatch: Dispatch<
    BasicStateAction<S>,
  > = (queue.dispatch = (dispatchAction.bind(
    //注意,因为 FunctionComponent 没有 this,所以 bind()第一个参数是 null
    null,
    // Flow doesn't know this is non-null, but we do.
    //当前正要渲染的 fiber 对象
    ((currentlyRenderingFiber: any): Fiber),
    queue,
  ): any));
  //initialState,dispatchAction.bind(null,currentlyRenderingFiber,queue,)
  //开发层面:const [name,setName]=React.useState('chen')
  //那么 name就是hook.memoizedState,赋值了'chen'
  //setName 就是 dispatch,即 dispatchAction.bind(null,currentlyRenderingFiber,queue,)
  return [hook.memoizedState, dispatch];
}
解析:

(1) 执行mountWorkInProgressHook(),获取最新的hook 链表

mountWorkInProgressHook()源码如下:

代码语言:javascript复制
//将当前 hook 加入 workInProgressHook 链表中,
//并返回最新的 hook 链表
function mountWorkInProgressHook(): Hook {
  const hook: Hook = {
    memoizedState: null,
    baseState: null,
    queue: null,
    baseUpdate: null,
    next: null,
  };

  if (workInProgressHook === null) {
    // This is the first hook in the list
    firstWorkInProgressHook = workInProgressHook = hook;
  } else {
    // Append to the end of the list
    workInProgressHook = workInProgressHook.next = hook;
  }
  //获取最新的 hook 链表,并返回
  return workInProgressHook;
}

(2) 如果initialState是一个callback的话,比如:

代码语言:javascript复制
const [styleObj, setStyleObj] = React.useState(()=>{return {height:14,} });

那么就会去执行initialState

代码语言:javascript复制
 //如果 initValue 是 function 的话,则获取执行的结果
  if (typeof initialState === 'function') {
    initialState = initialState();
  }

(3) 然后初始化了hook.memoizedStatehook.baseStatehook.queuequeue.dispatch

(4) basicStateReducer的源码如下:

代码语言:javascript复制
//传入参数 A、B,如果 B 是 function,则返回 B(A),否则返回 B
function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
  return typeof action === 'function' ? action(state) : action;
}

(5) 注意下dispatchAction()的使用:

代码语言:javascript复制
dispatchAction.bind(
    //注意,因为 FunctionComponent 没有 this,所以 bind()第一个参数是 null
    null,
    // Flow doesn't know this is non-null, but we do.
    //当前正要渲染的 fiber 对象
    ((currentlyRenderingFiber: any): Fiber),
    queue,
  )

因为 React-Hooks 只适用于FunctionComponent,所以绑定了thisnull,并且传入了参数——currentlyRenderingFiberqueue

(6) 最终返回一个数组:

代码语言:javascript复制
[hook.memoizedState, dispatch]

在开发层面 chen赋值给了hook.memoizedState

setName表示dispatch注意,这里 setName 只是形参,并没有赋值给 dispatch

(7) 此时的name 为'chen',当点击 div,调用setName('jin')时,会执行dispatchAction('jin')函数,我们来看下dispatchAction源码

二、dispatchAction()

作用:

(1) 新建 update 对象:{action:'jin'} (2) 将 update 加至 hook.queue 的末尾:hook.queue.last = update (3) 执行 scheduleWork(),走 updateFunctionComponent() 流程

源码:
代码语言:javascript复制
//1、新建 update 对象:{action:'jin'}
//2、将 update 加至 hook.queue 的末尾:hook.queue.last = update
//3、执行 scheduleWork(),走 updateFunctionComponent() 流程
function dispatchAction<S, A>(
  fiber: Fiber, //当前正要渲染的 fiber 对象
  queue: UpdateQueue<S, A>,
  action: A, //'jin'
) {
  invariant(
    numberOfReRenders < RE_RENDER_LIMIT,
    'Too many re-renders. React limits the number of renders to prevent '  
      'an infinite loop.',
  );

  //删除了 dev 代码

  const alternate = fiber.alternate;
  //由于这边的currentlyRenderingFiber为 null,传进来的fiber(currentlyRenderingFiber)有值,所以走 else 情况
  if (
    fiber === currentlyRenderingFiber ||
    (alternate !== null && alternate === currentlyRenderingFiber)
  ) {
    // This is a render phase update. Stash it in a lazily-created map of
    // queue -> linked list of updates. After this render pass, we'll restart
    // and apply the stashed updates on top of the work-in-progress hook.
    didScheduleRenderPhaseUpdate = true;
    const update: Update<S, A> = {
      expirationTime: renderExpirationTime,
      suspenseConfig: null,
      action,
      eagerReducer: null,
      eagerState: null,
      next: null,
    };
    if (renderPhaseUpdates === null) {
      renderPhaseUpdates = new Map();
    }
    const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
    //获取update,没有则初始化
    if (firstRenderPhaseUpdate === undefined) {
      renderPhaseUpdates.set(queue, update);
    }
    //如果有 update 的话,则遍历至 update 队尾,将最新的 update 添加至队尾
    else {
      // Append the update to the end of the list.
      let lastRenderPhaseUpdate = firstRenderPhaseUpdate;
      while (lastRenderPhaseUpdate.next !== null) {
        lastRenderPhaseUpdate = lastRenderPhaseUpdate.next;
      }
      lastRenderPhaseUpdate.next = update;
    }
  }

  else {
    if (revertPassiveEffectsChange) {
      flushPassiveEffects();
    }

    const currentTime = requestCurrentTime();
    const suspenseConfig = requestCurrentSuspenseConfig();
    const expirationTime = computeExpirationForFiber(
      currentTime,
      fiber,
      suspenseConfig,
    );
    //新建 update 对象
    //注意 action 就是传进来要更新的 state->'jin'
    const update: Update<S, A> = {
      expirationTime,
      suspenseConfig,
      action,
      eagerReducer: null,
      eagerState: null,
      next: null,
    };

    // Append the update to the end of the list.
    //将 update 加至 hook.queue 的末尾
    const last = queue.last;
    if (last === null) {
      // This is the first update. Create a circular list.
      update.next = update;
    } else {
      const first = last.next;
      if (first !== null) {
        // Still circular.
        update.next = first;
      }
      last.next = update;
    }
    queue.last = update;
    //fiber的优先级expirationTime为 0,和NoWork值相等,
    //并且alternate(也就是fiber的副本)的expirationTime也为 0,所以条件成立
    if (
      fiber.expirationTime === NoWork &&
      (alternate === null || alternate.expirationTime === NoWork)
    ) {
      // The queue is currently empty, which means we can eagerly compute the
      // next state before entering the render phase. If the new state is the
      // same as the current state, we may be able to bail out entirely.
      const lastRenderedReducer = queue.lastRenderedReducer;
      if (lastRenderedReducer !== null) {
        let prevDispatcher;

        //删除了 dev 代码

        try {
          const currentState: S = (queue.lastRenderedState: any);
          const eagerState = lastRenderedReducer(currentState, action);
          // Stash the eagerly computed state, and the reducer used to compute
          // it, on the update object. If the reducer hasn't changed by the
          // time we enter the render phase, then the eager state can be used
          // without calling the reducer again.

          //queue.last 也会同步更新,因为是同一引用地址
          update.eagerReducer = lastRenderedReducer;
          // update.eagerState = 'jin'
          update.eagerState = eagerState;
          if (is(eagerState, currentState)) {
            // Fast path. We can bail out without scheduling React to re-render.
            // It's still possible that we'll need to rebase this update later,
            // if the component re-renders for a different reason and by that
            // time the reducer has changed.
            return;
          }
        } catch (error) {
          // Suppress the error. It will throw again in the render phase.
        } finally {
          if (__DEV__) {
            ReactCurrentDispatcher.current = prevDispatcher;
          }
        }
      }
    }

    //删除了 dev 代码
    //最后执行scheduleWork(),之后会到 updateFunctionComponent 那边
    //关于 scheduleWork 的讲解,请看:
    //[React源码解析之scheduleWork(上)](https://juejin.im/post/5d7fa983f265da03cf7ac048)
    //[React源码解析之scheduleWork(下)](https://juejin.im/post/5d885b75f265da03e83baaa7)
    scheduleWork(fiber, expirationTime);
  }
}
解析:

(1) 首先注意下传进来的fiber和这里面的currentlyRenderingFiber不是同一个fiber 此时currentlyRenderingFiber为 null,因为还没有进行FunctionComponent的更新 但传进来的fiber是有值的,也就是App()

(2) 所以,以下条件不成立:

代码语言:javascript复制
  //由于这边的currentlyRenderingFiber为 null,传进来的fiber(currentlyRenderingFiber)有值,所以走 else 情况
  if (
    fiber === currentlyRenderingFiber ||
    (alternate !== null && alternate === currentlyRenderingFiber)
  ) {

  }

看下 else 情况

(3) 新建 update 对象:

代码语言:javascript复制
    const update: Update<S, A> = {
      expirationTime,
      suspenseConfig,
      action,
      eagerReducer: null,
      eagerState: null,
      next: null,
    };

注意: action就是传进来要更新的state—>'jin'

(4) 将update对象加至hook.queue的末尾

代码语言:javascript复制
    //将 update 加至 hook.queue 的末尾
    const last = queue.last;
    if (last === null) {
      // This is the first update. Create a circular list.
      update.next = update;
    } else {
      const first = last.next;
      if (first !== null) {
        // Still circular.
        update.next = first;
      }
      last.next = update;
    }
    queue.last = update;

(5) fiber的优先级expirationTime为 0,和NoWork值相等,并且alternate(也就是fiber的副本)的expirationTime也为 0,所以条件成立

代码语言:javascript复制
  if (
      fiber.expirationTime === NoWork &&
      (alternate === null || alternate.expirationTime === NoWork)
    ) {

  }

(6) lastRenderedReducer一、mountState()中「解析」的(4)提到过,是有值的,所以走这边:

代码语言:javascript复制
if (lastRenderedReducer !== null) {

}

(7) 以下代码的目的是将'jin'赋值到update.eagerState中,方便之后用到:

代码语言:javascript复制
  const currentState: S = (queue.lastRenderedState: any);
          const eagerState = lastRenderedReducer(currentState, action);
          //queue.last 也会同步更新,因为是同一引用地址
          update.eagerReducer = lastRenderedReducer;
          // update.eagerState = 'jin'
          update.eagerState = eagerState;

(8) 最后执行scheduleWork()方法,为什么之后会到updateFunctionComponent那边,可以自行断点调试查看:

(9) 关于scheduleWork()的讲解,请看: React源码解析之scheduleWork(上) React源码解析之scheduleWork(下)

三、从updateFunctionComponent()到updateState()

(1) updateFunctionComponent()源码中,有一个renderWithHooks()方法:

代码语言:javascript复制
  //在渲染的过程中,对里面用到的 hooks 函数做一些操作
    nextChildren = renderWithHooks(
      current,
      workInProgress,
      Component,
      nextProps,
      context,
      renderExpirationTime,
    );

(2) renderWithHooks()中有一个Component()方法:

代码语言:javascript复制
  //workInProgress.type,这里能当做 function 使用,说明 type 是 function
  //App()
  let children = Component(props, refOrContext);

注意: 这里的Component,是workInProgress.type,也就是App方法:

代码语言:javascript复制
function App() {
  debugger
  const [name, setName] = React.useState( 'chen');

  return (
      <div onClick={()=>setName('jin')}>
        {name}
      </div>
  );
}

执行App()后,又会到const [name, setName] = React.useState( 'chen');上,但此时的useState调用的不是源码中的mountState(),而是updateState('chen')!!

四、updateState()

作用:

多次更新initialState的值

源码:
代码语言:javascript复制
//多次更新 state 走这里
function updateState<S>(
  initialState: (() => S) | S, //'chen'
): [S, Dispatch<BasicStateAction<S>>] {
  //basicStateReducer 源码:action === 'function' ? action(state) : action;
  return updateReducer(basicStateReducer, (initialState: any));
}
解析:

(1) 可以看到,此时的updateState,其实是调用的updateReducer也就是说: 此时,useState()的本质是useReducer()

关于useReducer()的作用及使用,请看: https://zh-hans.reactjs.org/docs/hooks-reference.html#usereducer

(2) 注意此时的initialState仍然是'chen'

五、updateReducer()

作用:

多次更新initialState的值

源码:
代码语言:javascript复制
//useState 多次更新时调用 updateReducer
//reducer:basicStateReducer
//initialArg:initialState
function updateReducer<S, I, A>(
  reducer: (S, A) => S,
  initialArg: I,
  init?: I => S,
): [S, Dispatch<A>] {
  //当前正在 update 的 hook
  //{
  // memoizedState: "chen"
  // baseState: "chen"
  // queue:{
  //  last:{
  //    expirationTime: 1073741823
  //    action: "jin",
  //    eagerState: "jin"
  //    next:是 last 对象,所以 queue 是单向链表
  //  }
  // }
  //}
  const hook = updateWorkInProgressHook();
  //hook 的更新队列
  const queue = hook.queue;
  invariant(
    queue !== null,
    'Should have a queue. This is likely a bug in React. Please file an issue.',
  );
  //()=>{ return typeof action === 'function' ? action(state) : action;}
  queue.lastRenderedReducer = reducer;
  //numberOfReRenders=0,下面不看
  if (numberOfReRenders > 0) {
    // This is a re-render. Apply the new render phase updates to the previous
    // work-in-progress hook.
    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 = hook.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);

        // 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;
        // Don't persist the state accumlated from the render phase updates to
        // the base state unless the queue is empty.
        // TODO: Not sure if this is the desired semantics, but it's what we
        // do for gDSFP. I can't remember why.
        if (hook.baseUpdate === queue.last) {
          hook.baseState = newState;
        }

        queue.lastRenderedState = newState;

        return [newState, dispatch];
      }
    }
    return [hook.memoizedState, dispatch];
  }

  // The last update in the entire queue
  //获取hook 更新队列上最新的 update 对象
  const last = queue.last;
  // The last update that is part of the base state.
  const baseUpdate = hook.baseUpdate; //baseUpdate = null
  const baseState = hook.baseState; //baseState = "chen"

  // Find the first unprocessed update.
  let first;
  //baseUpdate = null
  if (baseUpdate !== null) {
    if (last !== null) {
      // For the first update, the queue is a circular linked list where
      // `queue.last.next = queue.first`. Once the first update commits, and
      // the `baseUpdate` is no longer empty, we can unravel the list.
      last.next = null;
    }
    first = baseUpdate.next;
  } else {
    first = last !== null ? last.next : null;
  }
  //first:{
  //   expirationTime: 1073741823
  //   action: "jin",
  //   eagerState: "jin"
  //   next:first 对象
  //  }
  if (first !== null) {
    let newState = baseState; //baseState = "chen"
    let newBaseState = null;
    let newBaseUpdate = null;
    let prevUpdate = baseUpdate; //baseUpdate = null
    let update = first; //object
    let didSkip = false;
    do {
      const updateExpirationTime = update.expirationTime; //1073741823
      //两者相等,跳过
      if (updateExpirationTime < renderExpirationTime) {
        // Priority is insufficient. Skip this update. If this is the first
        // skipped update, the previous update/state is the new base
        // update/state.
        if (!didSkip) {
          didSkip = true;
          newBaseUpdate = prevUpdate;
          newBaseState = newState;
        }
        // Update the remaining priority in the queue.
        if (updateExpirationTime > remainingExpirationTime) {
          remainingExpirationTime = updateExpirationTime;
        }
      }
      //走这里
      else {
        // This update does have sufficient priority.

        // Mark the event time of this update as relevant to this render pass.
        // TODO: This should ideally use the true event time of this update rather than
        // its priority which is a derived and not reverseable value.
        // TODO: We should skip this update if it was already committed but currently
        // we have no way of detecting the difference between a committed and suspended
        // update here.
        markRenderEventTimeAndConfig(
          updateExpirationTime,
          update.suspenseConfig,
        );

        // Process this update.
        // update.eagerReducer : basicStateReducer function
        // reducer : basicStateReducer function
        //两者相同,走这里
        //获取 update 的 state 值,新值
        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); //eagerState: "jin"

        } else {
          const action = update.action;
          newState = reducer(newState, action);
        }
      }
      prevUpdate = update;
      update = update.next;
      //update.next 就是 last 对象,last 对象就是 first,两者相等,跳出循环
    } while (update !== null && update !== first);

    if (!didSkip) {
      newBaseUpdate = prevUpdate;
      newBaseState = newState;
    }

    // Mark that the fiber performed work, but only if the new state is
    // different from the current state.
    //判断 memoizedState(oldState) 和 newState 是否真的不同
    if (!is(newState, hook.memoizedState)) {
      //标记当前 fiber 的确收到了 update
      markWorkInProgressReceivedUpdate();
    }

    hook.memoizedState = newState; //newState = "jin"
    hook.baseUpdate = newBaseUpdate;
    hook.baseState = newBaseState; //newBaseState = "jin"

    queue.lastRenderedState = newState;
  }

  const dispatch: Dispatch<A> = (queue.dispatch: any);
  //[name,setName]
  //dispatchAction
  return [hook.memoizedState, dispatch];
}
解析:

(1) 执行updateWorkInProgressHook(),获取当前正在updatehook

代码语言:javascript复制
  const hook = updateWorkInProgressHook();

简单看下updateWorkInProgressHook()的源码:

代码语言:javascript复制
//当前正在 update 的 fiber 上的 hook
function updateWorkInProgressHook(): Hook {
  // This function is used both for updates and for re-renders triggered by a
  // render phase update. It assumes there is either a current hook we can
  // clone, or a work-in-progress hook from a previous render pass that we can
  // use as a base. When we reach the end of the base list, we must switch to
  // the dispatcher used for mounts.
  if (nextWorkInProgressHook !== null) {
    // There's already a work-in-progress. Reuse it.
    workInProgressHook = nextWorkInProgressHook;
    nextWorkInProgressHook = workInProgressHook.next;

    currentHook = nextCurrentHook;
    nextCurrentHook = currentHook !== null ? currentHook.next : null;
  } else {
    // Clone from the current hook.
    invariant(
      nextCurrentHook !== null,
      'Rendered more hooks than during the previous render.',
    );
    currentHook = nextCurrentHook;

    const newHook: Hook = {
      memoizedState: currentHook.memoizedState,

      baseState: currentHook.baseState,
      queue: currentHook.queue,
      baseUpdate: currentHook.baseUpdate,

      next: null,
    };

    if (workInProgressHook === null) {
      // This is the first hook in the list.
      workInProgressHook = firstWorkInProgressHook = newHook;
    } else {
      // Append to the end of the list.
      workInProgressHook = workInProgressHook.next = newHook;
    }
    nextCurrentHook = currentHook.next;
  }
  return workInProgressHook;
}

最后返回的workInProgressHook,大概是这样:

代码语言:javascript复制
  const workInProgressHook={
  memoizedState: "chen",
  baseState: "chen",
  queue:{
   last:{
     expirationTime: 1073741823,
     action: "jin",
     eagerState: "jin",
     next:  //是 last 对象,所以 queue 是单向链表
   }
  }
  }

(2) hook.queuehook的更新队列,我们需要更新的值就在queue.last. eagerState/action中:

代码语言:javascript复制
  const queue = hook.queue;

(3) numberOfReRenders表示重新渲染时fiber的节点数,也就是在render 的时候,又产生了 update 时,会加 1,但这里的numberOfReRenders为 0,所以不走这边:

代码语言:javascript复制
if (numberOfReRenders > 0) {

}

(4) 赋值firsthook.queue.last.next

代码语言:javascript复制
  // Find the first unprocessed update.
  let first;
  //baseUpdate = null
  if (baseUpdate !== null) {
    if (last !== null) {
      // For the first update, the queue is a circular linked list where
      // `queue.last.next = queue.first`. Once the first update commits, and
      // the `baseUpdate` is no longer empty, we can unravel the list.
      last.next = null;
    }
    first = baseUpdate.next;
  } else {
    first = last !== null ? last.next : null;
  }

first大概长这样:

代码语言:javascript复制
  const first={
    expirationTime: 1073741823,
    action: "jin",
    eagerState: "jin",
    next:  //还是first 对象
   }

(5) 因为first不为 null,所以走这边

代码语言:javascript复制
if (first !== null) {
    let newState = baseState; //baseState = "chen"
    let newBaseState = null;
    let newBaseUpdate = null;
    let prevUpdate = baseUpdate; //baseUpdate = null
    let update = first; //object
    let didSkip = false;
    do {
      const updateExpirationTime = update.expirationTime; //1073741823
      //两者相等,跳过
      if (updateExpirationTime < renderExpirationTime) {
        // Priority is insufficient. Skip this update. If this is the first
        // skipped update, the previous update/state is the new base
        // update/state.
        if (!didSkip) {
          didSkip = true;
          newBaseUpdate = prevUpdate;
          newBaseState = newState;
        }
        // Update the remaining priority in the queue.
        if (updateExpirationTime > remainingExpirationTime) {
          remainingExpirationTime = updateExpirationTime;
        }
      }
      //走这里
      else {
        // This update does have sufficient priority.

        // Mark the event time of this update as relevant to this render pass.
        // TODO: This should ideally use the true event time of this update rather than
        // its priority which is a derived and not reverseable value.
        // TODO: We should skip this update if it was already committed but currently
        // we have no way of detecting the difference between a committed and suspended
        // update here.
        markRenderEventTimeAndConfig(
          updateExpirationTime,
          update.suspenseConfig,
        );

        // Process this update.
        // update.eagerReducer : basicStateReducer function
        // reducer : basicStateReducer function
        //两者相同,走这里
        //获取 update 的 state 值,新值
        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); //eagerState: "jin"

        } else {
          const action = update.action;
          newState = reducer(newState, action);
        }
      }
      prevUpdate = update;
      update = update.next;
      //update.next 就是 last 对象,last 对象就是 first,两者相等,跳出循环
    } while (update !== null && update !== first);

    if (!didSkip) {
      newBaseUpdate = prevUpdate;
      newBaseState = newState;
    }

    // Mark that the fiber performed work, but only if the new state is
    // different from the current state.
    //判断 memoizedState(oldState) 和 newState 是否真的不同
    if (!is(newState, hook.memoizedState)) {
      //标记当前 fiber 的确收到了 update
      markWorkInProgressReceivedUpdate();
    }

    hook.memoizedState = newState; //newState = "jin"
    hook.baseUpdate = newBaseUpdate;
    hook.baseState = newBaseState; //newBaseState = "jin"

    queue.lastRenderedState = newState;
  }

注意这边是一个do..while循环,跳出条件是:

代码语言:javascript复制
!(update !== null && update !== first)

while循环中,因为updateExpirationTime是和renderExpirationTime相等的,因为:

代码语言:javascript复制
export function renderWithHooks(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any, //render
  props: any,
  refOrContext: any, //ref
  nextRenderExpirationTime: ExpirationTime,
):{
  //本次 render 时候的优先级
  renderExpirationTime = nextRenderExpirationTime;
  //...
}

所以不走这边:

代码语言:javascript复制
  //两者相等,跳过
  if (updateExpirationTime < renderExpirationTime) {

  }

(6) 然后走这里:

代码语言:javascript复制
//走这里
      else {
        //删除了一些代码

        // Process this update.
        // update.eagerReducer : basicStateReducer function
        // reducer : basicStateReducer function
        //两者相同,走这里
        //获取 update 的 state 值,新值
        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); //eagerState: "jin"

        } else {
          const action = update.action;
          newState = reducer(newState, action);
        }
      }

因为update.eagerState等于二、dispatchAction()中的queue.lastRenderedReducer,等于一、mountState()中的basicStateReducer,所以update.eagerReducer === reducer条件成立,走这边:

代码语言:javascript复制
newState = ((update.eagerState: any): S);

此时newState=update.eagerState='jin',然后因为update.next就是last对象,last对象就是first,两者相等,跳出while循环

(7) 跳出while循环后走这边:

代码语言:javascript复制
    hook.memoizedState = newState; //newState = "jin"
    hook.baseUpdate = newBaseUpdate;
    hook.baseState = newBaseState; //newBaseState = "jin"

  }

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

'jin'赋值给hook.memoizedState,返回['jin', dispatch]

(8) 到开发层面,此时name已更新为'jin'

代码语言:javascript复制
function App() {
  debugger
  const [name, setName] = React.useState( 'chen');

  return (
      <div onClick={()=>setName('jin')}>
        {name}
      </div>
  );
}

useState源码解析部分就结束了,接下来看下 下面这个问题

六、为什么useState要按顺序执行

如果你时常查阅 React 官方文档的话,肯定会看到这个规则:

https://zh-hans.reactjs.org/docs/hooks-rules.html#explanation

为什么useState要按顺序执行呢?

我们注意下mountWorkInProgressHook()firstWorkInProgressHook

由图可以看到,当初始化三个 useState 时,Hooks链是通过next来绑定三个state的顺序的,如果在多次调用 Hooks 时,将某一个useState有条件的省略掉,不执行,那么.next的时候,获取的 state 就不是理想的 state,会造成state错位,所以 React 官方已经禁止这样做了:

七、useState流程图

useState流程图useState流程图

八、GitHub

mountState/dispatchAction/renderWithHooks/updateState/updateReducerhttps://github.com/AttackXiaoJinJin/reactExplain/blob/master/react16.8.6/packages/react-reconciler/src/ReactFiberHooks.js

updateFunctionComponenthttps://github.com/AttackXiaoJinJin/reactExplain/blob/master/react16.8.6/packages/react-reconciler/src/ReactFiberBeginWork.js


0 人点赞