banner
IWSR

IWSR

我永远喜欢志喜屋梦子!

React Hooks: useReducer 分析 React Hooks(リアクトフックス): useReducer(ユーズリデューサー)の分析

説明#

  1. この記事は v18.1.0 を基に分析されています。
  2. この記事を読むには、まずReact Hooks: hooks 链表React Hooks: useState 分析を読んでください。
  3. 他のフックと同様に、マウントとアップデートのフェーズに分かれます。
  4. useState とほぼ同じですので、多くの内容は useState を参考にすることができます。
  5. 分析は公式デモを基にしています。
import { useReducer } from "react";

function init(initialCount) {
  return {count: initialCount};
}

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    case 'reset':
      return init(action.payload);
    default:
      throw new Error();
  }
}

function Counter({initialCount}) {
  const [state, dispatch] = useReducer(reducer, initialCount, init);
  return (
    <>
      Count: {state.count}
      <button
        onClick={() => dispatch({type: 'reset', payload: initialCount})}>
        Reset
      </button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

export default Counter;

マウントシーンでの useReducer#

useReducer の初期化時に入ります。

image

function mountReducer<S, I, A>(
  reducer: (S, A) => S,
  initialArg: I,
  init?: I => S,
): [S, Dispatch<A>] {
  // フックオブジェクトを生成し、リンクリストに追加します
  const hook = mountWorkInProgressHook();
  let initialState;
  // 初期化関数が存在するかどうかを確認します
  if (init !== undefined) {
    // initを使用してinitialArgを処理します
    initialState = init(initialArg);
  } else {
    // 直接代入します
    initialState = ((initialArg: any): S);
  }
  // 初期値をフックオブジェクトに追加します
  hook.memoizedState = hook.baseState = initialState;
  // useStateと同様に、updateを管理するためのキューがあります
  // 詳細はuseStateの解析を参照してください
  const queue: UpdateQueue<S, A> = {
    pending: null,
    interleaved: null,
    lanes: NoLanes,
    dispatch: null,
    lastRenderedReducer: reducer,
    lastRenderedState: (initialState: any),
  };
  hook.queue = queue;
  /**
   * useStateと同様に、dispatchを返しますが、ここではdispatchReducerActionを返します。
   * useStateはdispatchSetStateを返しますが、ほとんど同じです。次に進んでみましょう
  */
  const dispatch: Dispatch<A> = (queue.dispatch = (dispatchReducerAction.bind(
    null,
    currentlyRenderingFiber,
    queue,
  ): any));
  return [hook.memoizedState, dispatch];
}

わかりますように、useReducer の実装は useState の実装とほとんど変わりません。もちろん後の部分もほぼ同じです。

useReducer の dispatch のトリガー#

任意の onClick イベントをトリガーすると、dispatchReducerAction に入ります。

dispatchSetState と非常に似ているため、ここでは異なる点のみ説明します。

function dispatchReducerAction<S, A>(
  fiber: Fiber,
  queue: UpdateQueue<S, A>,
  action: A,
) {
  ...

  const lane = requestUpdateLane(fiber);

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

  if (isRenderPhaseUpdate(fiber)) {
    enqueueRenderPhaseUpdate(queue, update);
  } else {
    /**
     * dispatchSetStateと比較すると、
     * dispatchReducerActionは最初のupdateオブジェクトを計算しません
    */
    const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
    if (root !== null) {
      const eventTime = requestEventTime();
      scheduleUpdateOnFiber(root, fiber, lane, eventTime);
      entangleTransitionUpdate(root, queue, lane);
    }
  }

  ...
}

他の部分はまったく同じです。

アップデートシーンでの useReducer#

useState のアップデートフェーズと同じです。すでに useState で説明したので、ここでは省略します。

まとめ#

useReducer の実装ロジックは useState とほぼ同じです。唯一の違いは以下の 2 点です。

  1. useReducer の dispatch は、最初の update オブジェクトが生成される際に期待値(eagerState)を計算しません。
  2. useReducer はマウントフェーズで useState と同様に basicStateReducer を生成せず、直接渡された Reducer を使用します。
読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。