LoginSignup
15
15

useReducerの仕様を噛み砕いて理解する

Last updated at Posted at 2023-10-24

「useReducerについて理解を深めたい!」
「いまいちピンとこない!」
「でぃすぱっち?難しそう!」

という方に...
私なりに噛み砕いてアウトプットしてみます。(useReducerの公式リファレンス)

useReducerの使い方

他のhooksと同様、トップレベルで呼び出す。

import { useReducer } from 'react';

function reducer関数(state, action) {
  switch (action.type) {
    case 'アクション':
      return 新しい値;
    // ...
    default:
      throw new Error('未知のアクションタイプ');
  }
}

function MyComponent() {
  // 引数の数により、2パターンある
  const [現在のstate, dispatch関数] = useReducer(reducer関数, 初期値);
  // または
  const [現在のstate, dispatch関数] = useReducer(reducer関数, 初期Arg, 初期化関数);
  // ...
}

useReducerの引数

第1引数 : reducer (りでゅーさー)

  • stateをどのように更新するかを指定するリデューサ関数
    • リデューサ?
      • 英語訳は減速機とか薄め液
      • JsではArray.prototype.reduce というのがあるので、そっちの意味に近い。
        • 現在のステートとアクションを受け取り、新しいステートを返す関数
          • つまり、複数を1つにまとめて(縮小)新たな値を出力すること。
    • アクション?
      • ステートをどのように変更するかを表現するもの(チョコになっちゃえ〜といった指示)

第2引数 : initialArg 2パターン

  • 初期値
  • 引数が第3引数(関数)まであると、第3引数に渡す引数となる

第3引数 : init

  • 初期化のための関数(後で説明)

useReducerの返り値

 const [現在のstate, dispatch関数] = useReducer(第1引数, 第2引数);

1 : 現在のstate
2 : stateを別の値に更新し、レンダーを発火するための関数(dispatch関数)

dispatch関数 ?

  onClick={dispatch({ type: 'チョコになっちゃえ' })}

ディスパッチされたアクションはreducer関数に渡され、その結果として新しいステートが返され、コンポーネントが再レンダリングされます

dispatch関数の引数

アクション

  • stateを別の値に変化させるための関数、dispatch関数には、唯一の引数としてアクションを渡す必要がある。
  • {type : 'アクション'} とする
    • アクションはuseReducerの第1引数に設置した関数内で使われる。(action.type)

返り値

  • 無し

dispatch関数の使用サンプル

  • 例 : dispatch関数は機械を操作するリモコンと思っておく。
    • 電源のオンオフ、温度の強弱の調整、前に進め、後退しろ、前に蹴れ...といった命令を飛ばす。
<button onClick={() => dispatch({ type: '電源ONOFF切り替えて' })}>電源</button>
<button onClick={() => dispatch({ type: '温度を1度上げる' })}>温度+1</button>
<button onClick={() => dispatch({ type: '風向きを自動にして' })}>風向き 自動</button>
import { useReducer } from 'react';

// 指示をうけとって、現在のstateを変更する(指示と値を混ぜて1つにする)
function reducer(state, action) {
  if (action.type === '温度を1度上げる') {
    // state.温度 = state.温度 + 1; returnの前に値を変更してはいけない!
    return {
      温度: state.温度 + 1 // returnの中で値を変更する
    };
  }
  throw Error('不明な操作です');
}

export default function Counter() {
  const [state, dispatch] = useReducer(reducer, { 温度: 23 });
  return (
    <>
      <button onClick={() => {
        dispatch({ type: '温度を1上げる' }) // reducerに対して指示を出すだけ
      }}>
        温度を1度上げる
      </button>
      <p>現在の温度は {state.温度}.</p>
    </>
  );
}

スクリーンショット 2023-10-13 9.10.30.png

初期状態

  • 第3引数 : init : こっちも初期値だけど関数(後で説明)

引数が2つの場合

  • initialStateはステートの直接的な初期値として使われます。
    • useReducer(関数, 初期値);

引数が3つの場合

  • initFunction(initialArg)が呼ばれ、その結果が初期ステートとして使われます。
    • useReducer(関数, 初期Arg, 初期化関数);

// useReducer引数が3つの場合

function 初期化関数(初期Arg) {
  // ここで初期値を処理(必要に応じて)
  return { ...初期値, 何かのプロパティ: '何かの値' };
}

function reducer関数(state, action) {
  switch (action.type) {
    case '特定のアクションタイプ':
      // 新しいステートを計算して返す
      return { ...state, 何かのプロパティ: '新しい値' };
    // ...
    default:
      throw new Error('未知のアクションタイプ');
  }
}

function MyComponent({ 初期値 }) { // 親から受け取った初期値をreducerに使いたいとか
  const [現在のstate, dispatch関数] = useReducer(reducer関数, 初期Arg, 初期化関数);
  // ...
  return (
      <button onClick={() => { dispatch({ type: '特定のアクションタイプ' })}}>特定のアクションタイプに変更して</button>
}

useReducerの初期化時っていつなの?

下記3つを初期化時と言います。

1, ページを開いたとき(ルーティングによるページ遷移)

  • ページにアクセスしたときにコンポーネントはマウントされ、初期化処理が行われます。

2, コンポーネントが動的に追加されたとき

  • 例えば、ユーザーのアクションに応じて特定のコンポーネントがページに追加される場合(モーダルウィンドウの表示、リストへのアイテムの追加など)

3, 親コンポーネントが再マウントされたとき
親コンポーネントが何らかの理由でアンマウントされ、再度マウントされた時

useState と useReducer どっちを選ぶ?

行いたい状態管理が簡単か、複雑かで選べば大丈夫です。

  • useState
    • アクションが少なく、ロジックが単純な場合。
    • 単一の状態変数を管理している場合。
    • 状態が互いに独立している、関連が少ない場合
  • useReducer
    • フォームの複数の入力フィールド、リストの並べ替えとフィルタリング、ゲームの状態管理
    • 複数のサブ値を含む場合。
    • 状態の更新ロジックをコンポーネントから分離したい場合。
    • 同じ状態変更ロジックを複数のコンポーネントで再利用したい場合。
    • アクションによって状態の更新方法を詳細に制御したい場合。

お疲れ様でした!
少しは理解が深まれば幸いです!
より理解を深めるために、公式のサンプルソースを読み解いていきましょう!!

15
15
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
15