Reactの内部実装を見ていくよ(useStateフック&ステート更新からのトリガーフェーズ)
やっていき
まずここを参考に
useState本体
export function useState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  const dispatcher = resolveDispatcher();
  return dispatcher.useState(initialState);
}
resolveDispatcher.js
function resolveDispatcher() {
  const dispatcher = ReactSharedInternals.H;
  if (__DEV__) {
    if (dispatcher === null) {
      console.error(
        'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
          ' one of the following reasons:\n' +
          '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
          '2. You might be breaking the Rules of Hooks\n' +
          '3. You might have more than one copy of React in the same app\n' +
          'See https://react.dev/link/invalid-hook-call for tips about how to debug and fix this problem.',
      );
    }
  }
  // Will result in a null access error if accessed outside render phase. We
  // intentionally don't throw our own error because this is in a hot path.
  // Also helps ensure this is inlined.
  return ((dispatcher: any): Dispatcher);
}
ReactSharedInternals.H;はどこからやってくるのじゃ〜〜〜〜、となる
なんとその答えは別パッケージにある
react-reconcilerを参照
renderWithHooks関数内
https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberHooks.js
export function renderWithHooks<Props, SecondArg>(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: (p: Props, arg: SecondArg) => any,
  props: Props,
  secondArg: SecondArg,
  nextRenderLanes: Lanes,
): any {
  renderLanes = nextRenderLanes;
  currentlyRenderingFiber = workInProgress;
(省略)
  if (__DEV__) {
    if (current !== null && current.memoizedState !== null) {
      ReactSharedInternals.H = HooksDispatcherOnUpdateInDEV;
    } else if (hookTypesDev !== null) {
      // This dispatcher handles an edge case where a component is updating,
      // but no stateful hooks have been used.
      // We want to match the production code behavior (which will use HooksDispatcherOnMount),
      // but with the extra DEV validation to ensure hooks ordering hasn't changed.
      // This dispatcher does that.
      ReactSharedInternals.H = HooksDispatcherOnMountWithHookTypesInDEV;
    } else {
      ReactSharedInternals.H = HooksDispatcherOnMountInDEV;
    }
  } else {
    ReactSharedInternals.H =
      current === null || current.memoizedState === null
        ? HooksDispatcherOnMount
        : HooksDispatcherOnUpdate;
  }
...
ざっくり
currentがnull もしくはcurrentがあってもmemorizedStateがnullなら
初回のマウント時だと判断してHooksDispatcherOnMountをいれる
そうじゃないなら二回目以降の更新時だと判断してHooksDispatcherOnUpdateをいれる
という感じ?
currentというのはFiberツリーのcurrentのことだろうか?違うかも
HooksDispatcherOnMountとは
const HooksDispatcherOnMount: Dispatcher = {
  readContext,
  use,
  useCallback: mountCallback,
  useContext: readContext,
  useEffect: mountEffect,
  useImperativeHandle: mountImperativeHandle,
  useLayoutEffect: mountLayoutEffect,
  useInsertionEffect: mountInsertionEffect,
  useMemo: mountMemo,
  useReducer: mountReducer,
  useRef: mountRef,
  useState: mountState,
  useDebugValue: mountDebugValue,
  useDeferredValue: mountDeferredValue,
  useTransition: mountTransition,
  useSyncExternalStore: mountSyncExternalStore,
  useId: mountId,
};
const HooksDispatcherOnUpdate: Dispatcher = {
  readContext,
  use,
  useCallback: updateCallback,
  useContext: readContext,
  useEffect: updateEffect,
  useImperativeHandle: updateImperativeHandle,
  useInsertionEffect: updateInsertionEffect,
  useLayoutEffect: updateLayoutEffect,
  useMemo: updateMemo,
  useReducer: updateReducer,
  useRef: updateRef,
  useState: updateState,
  useDebugValue: updateDebugValue,
  useDeferredValue: updateDeferredValue,
  useTransition: updateTransition,
  useSyncExternalStore: updateSyncExternalStore,
  useId: updateId,
};
え、なにこれは
とりあえず、useStateの処理を追いたい場合にはmountState関数かupdateState関数を追跡すればいいらしい
- 初回のマウント時の場合
HooksDispatcherOnMountはmountState関数 - 二回目の更新時の場合
HooksDispatcherOnUpdateはupdateState関数 
mountState/updateStateはどこから?
function mountState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  const hook = mountStateImpl(initialState);
  const queue = hook.queue;
  const dispatch: Dispatch<BasicStateAction<S>> = (dispatchSetState.bind(
    null,
    currentlyRenderingFiber,
    queue,
  ): any);
  queue.dispatch = dispatch;
  return [hook.memoizedState, dispatch];
}
function updateState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  return updateReducer(basicStateReducer, initialState);
}
いよいよここにたどり着いた
まずmountStateの感想
- mountStateImplでフックのオブジェクトを取得している?
 - dispatchSetStateで現在レンダリング中のFiberオブジェクトとフックの持つキューをバインドして、更新用関数を作成している?
 - ステートの閲覧する方はフック内部のmemorizedStateというプロパティを参照している
 - 配列に入れて返却!いつもよく見るステートの戻り値や!
 
mountStateImplとは何をしているのか?
function mountStateImpl<S>(initialState: (() => S) | S): Hook {
  const hook = mountWorkInProgressHook();
  if (typeof initialState === 'function') {
    const initialStateInitializer = initialState;
    // $FlowFixMe[incompatible-use]: Flow doesn't like mixed types
    initialState = initialStateInitializer();
    if (shouldDoubleInvokeUserFnsInHooksDEV) {
      setIsStrictModeForDevtools(true);
      try {
        // $FlowFixMe[incompatible-use]: Flow doesn't like mixed types
        initialStateInitializer();
      } finally {
        setIsStrictModeForDevtools(false);
      }
    }
  }
  hook.memoizedState = hook.baseState = initialState;
  const queue: UpdateQueue<S, BasicStateAction<S>> = {
    pending: null,
    lanes: NoLanes,
    dispatch: null,
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: (initialState: any),
  };
  hook.queue = queue;
  return hook;
}
とりあえずまず呼び出されているmountWorkInProgressHookとは?
function mountWorkInProgressHook(): Hook {
  const hook: Hook = {
    memoizedState: null,
    baseState: null,
    baseQueue: null,
    queue: null,
    next: null,
  };
  if (workInProgressHook === null) {
    // This is the first hook in the list
    currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
  } else {
    // Append to the end of the list
    workInProgressHook = workInProgressHook.next = hook;
  }
  return workInProgressHook;
}
やってること
現在のフックが初呼び出しなら
workInProgressHookとcurrentlyRenderingFiber.memoizedStateに現在のフックを代入
現在のフックが二個目以降のフックなら
workInProgressHookとworkInProgressHook.nextに現在のフックを代入←???
workInProgressHookを返却
ここでヒント!
export type Hook = {
  memoizedState: any,
  baseState: any,
  baseQueue: Update<any, any> | null,
  queue: any,
  next: Hook | null,
};
フックの型はこうなっているっぽい
hook.memoizedState: メモリに保持されているローカルな状態。
hook.baseState: hook.baseQueue内のすべてのアップデートオブジェクトがマージされた後の状態。
hook.baseQueue: 現在のレンダリング優先度よりも高いものだけを含む、アップデートオブジェクトの循環的なチェーン。
hook.queue: 優先度の高いすべてのアップデートオブジェクトを含む、アップデートオブジェクトの循環的なチェーン。
hook.next: 次のポインタ、チェーンの次のフックを指します。
つまり、フックオブジェクトは連結リストになっているっぽい
なお、nextはnull許容なので、次のフックオブジェクトがない場合も当然ある
なんとなく、フックの呼び出しが連結リストになっていることは把握したかも?
では、mountWorkInProgressHookの調査を終わり、mountStateImplに戻る
コード再掲
function mountStateImpl<S>(initialState: (() => S) | S): Hook {
  const hook = mountWorkInProgressHook();
  if (typeof initialState === 'function') {
    const initialStateInitializer = initialState;
    // $FlowFixMe[incompatible-use]: Flow doesn't like mixed types
    initialState = initialStateInitializer();
    if (shouldDoubleInvokeUserFnsInHooksDEV) {
      setIsStrictModeForDevtools(true);
      try {
        // $FlowFixMe[incompatible-use]: Flow doesn't like mixed types
        initialStateInitializer();
      } finally {
        setIsStrictModeForDevtools(false);
      }
    }
  }
  hook.memoizedState = hook.baseState = initialState;
  const queue: UpdateQueue<S, BasicStateAction<S>> = {
    pending: null,
    lanes: NoLanes,
    dispatch: null,
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: (initialState: any),
  };
  hook.queue = queue;
  return hook;
}
感想
- initialStateはuseStateで最初に渡す値な気がする
- コールバック関数が渡されてたらそれを実行する?
 
 
useState(() => うんちゃら) //みたいな?
- initialStateを、memorizedStateとmemorizedStateに代入
 - hook.queueに空っぽのキューを代入して初期化
 - 完成したフックを返却
 
  const queue: UpdateQueue<S, BasicStateAction<S>> = {
    pending: null,
    lanes: NoLanes,
    dispatch: null,
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: (initialState: any),
  };
laneはおなじみレーンのことかな
mountStateImplの調査を終わらせて、mountStateの調査に戻る
コード再掲
function mountState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  const hook = mountStateImpl(initialState);
  const queue = hook.queue;
  const dispatch: Dispatch<BasicStateAction<S>> = (dispatchSetState.bind(
    null,
    currentlyRenderingFiber,
    queue,
  ): any);
  queue.dispatch = dispatch;
  return [hook.memoizedState, dispatch];
}
今度はdispatchSetStateが気になるので見てみる
dispatchSetState関数
function dispatchSetState<S, A>(
  fiber: Fiber,
  queue: UpdateQueue<S, A>,
  action: A,
): void {
  if (__DEV__) {
    if (typeof arguments[3] === 'function') {
      console.error(
        "State updates from the useState() and useReducer() Hooks don't support the " +
          'second callback argument. To execute a side effect after ' +
          'rendering, declare it in the component body with useEffect().',
      );
    }
  }
  const lane = requestUpdateLane(fiber);
  const didScheduleUpdate = dispatchSetStateInternal(
    fiber,
    queue,
    action,
    lane,
  );
  if (didScheduleUpdate) {
    startUpdateTimerByLane(lane);
  }
  markUpdateInDevTools(fiber, lane, action);
}
実質的な処理はdispatchSetStateInternalに全部書いてありそう・・・
dispatchSetStateInternal関数
function dispatchSetStateInternal<S, A>(
  fiber: Fiber,
  queue: UpdateQueue<S, A>,
  action: A,
  lane: Lane,
): boolean {
  const update: Update<S, A> = {
    lane,
    revertLane: NoLane,
    action,
    hasEagerState: false,
    eagerState: null,
    next: (null: any),
  };
  if (isRenderPhaseUpdate(fiber)) {
    enqueueRenderPhaseUpdate(queue, update);
  } else {
    const alternate = fiber.alternate;
    if (
      fiber.lanes === NoLanes &&
      (alternate === null || alternate.lanes === NoLanes)
    ) {
      // 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 = null;
        if (__DEV__) {
          prevDispatcher = ReactSharedInternals.H;
          ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV;
        }
        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.
          update.hasEagerState = true;
          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.
            // TODO: Do we still need to entangle transitions in this case?
            enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update);
            return false;
          }
        } catch (error) {
          // Suppress the error. It will throw again in the render phase.
        } finally {
          if (__DEV__) {
            ReactSharedInternals.H = prevDispatcher;
          }
        }
      }
    }
    const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
    if (root !== null) {
      scheduleUpdateOnFiber(root, fiber, lane);
      entangleTransitionUpdate(root, queue, lane);
      return true;
    }
  }
  return false;
}
見てみた感想
- まずupdateオブジェクトを作成
 
  const update: Update<S, A> = {
    lane,
    revertLane: NoLane,
    action,
    hasEagerState: false,
    eagerState: null,
    next: (null: any),
  };
これってこのupdateオブジェクトと同じか!
これも連結リストの構造になっているのは面白いかも
  if (isRenderPhaseUpdate(fiber)) {
    enqueueRenderPhaseUpdate(queue, update);
  }
- isRenderPhaseUpdate関数でレンダリング中かどうかを判定
 - レンダリング中の場合は更新キューに追加
 
    enqueueRenderPhaseUpdate(queue, update);
- レンダリング中でないならenqueueConcurrentHookUpdateAndEagerlyBailout関数呼び出し?
これは何をしてるのかわからなかったが、今回は無視!
(balioutという処理を動かしているっぽい。ハック) 
// 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.
// TODO: Do we still need to entangle transitions in this case?
- どっちみちenqueueConcurrentHookUpdate関数は実行される
 - rootがnullでないならscheduleUpdateOnFiberとentangleTransitionUpdateを実行
 
    const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
    if (root !== null) {
      scheduleUpdateOnFiber(root, fiber, lane);
      entangleTransitionUpdate(root, queue, lane);
      return true;
    }
enqueueRenderPhaseUpdate関数を軽く確認
function enqueueRenderPhaseUpdate<S, A>(
  queue: UpdateQueue<S, A>,
  update: Update<S, A>,
): void {
  // 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.
  didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate =
    true;
  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オブジェクト キューなので一番最後尾
 - pendingがnullなら (空っぽ)
- nextにはupdateを指定
 
 - そうじゃないなら
- 最後尾に並ぶ
 - updateオブジェクトのnextをpendingのnextとして指定
- 循環リストなのでpending.nextは最前列のupdateオブジェクト
 
 - pendingのnextに今のupdateオブジェクトの参照をいれる
 - キューのpendingを今のupdateオブジェクトであると更新
 
 
enqueueConcurrentHookUpdate関数
export function enqueueConcurrentHookUpdate<S, A>(
  fiber: Fiber,
  queue: HookQueue<S, A>,
  update: HookUpdate<S, A>,
  lane: Lane,
): FiberRoot | null {
  const concurrentQueue: ConcurrentQueue = (queue: any);
  const concurrentUpdate: ConcurrentUpdate = (update: any);
  enqueueUpdate(fiber, concurrentQueue, concurrentUpdate, lane);
  return getRootForUpdatedFiber(fiber);
}
enqueueUpdate関数のラッパー?
enqueueUpdate関数の詳細は以下を参照
ここでフックのupdateキューをFiberの持つupdateキューに追加しているということだろうか?
enqueueRenderPhaseUpdate関数は満足
mountState関数も満足
一通り見てきた感じがする
ここから気になること
- キューに入れられたupdateオブジェクトはどのように拾われるの?
 - 初回マウント時しか見れてないけど更新時はどうなるんだろう
 
一気に解決していく
scheduleUpdateOnFiber関数で再レンダリングをスケジュールしているっぽい
ここからはreact scheduler側のコードになるっぽい
scheduleUpdateOnFiber関数は、トリガーを行うためのきっかけになる関数っぽい

ここ以降はまた別のスクラップで確認することに
また、renderWithHooks関数はどこから呼び出されるのか
→これはレンダーフェーズ編で解説する!