1. ホーム
  2. 記事一覧
  3. useReducerとは?状態管理でuseStateと迷わない使い分けを解説

2026.03.14

useReducerとは?状態管理でuseStateと迷わない使い分けを解説

useReducerは、Reactで複雑な状態管理を扱いやすくするためのHookです。useStateだけでは処理の見通しが悪くなりがちな場面で、状態の更新ロジックを1つにまとめて扱えるようになります。

この記事で学べること:

  • useReducerの基本的な仕組みと処理の流れ
  • useStateとの違いと使い分けの判断基準
  • useReducerを使ったTodoアプリの実装例

useReducerを理解することで、状態管理の手段がuseStateだけに限られなくなり、機能の複雑さに応じて適切なHookを選べるようになります。「この場面ではどちらを使うべきか」を自分で判断できる力は、React開発の実践で大きな武器になります。

Reactの開発環境の構築方法や、基本を復習したい方は以下の記事も参考にしてください。

useReducerとは

useReducerとは、Reactコンポーネントの状態(state)を管理するためのHookです。useStateと同じく状態管理に使いますが、状態が複雑な場合や、複数の状態更新パターンを1つの関数にまとめて管理したい場合に適しています。

useReducerの基本

useReducerは、以下の4つの要素で構成されています。

  • 状態(state)

    コンポーネント内で管理するデータや情報です。

  • アクション(action)

    状態をどのように更新するかを指定するオブジェクトや値です。通常、typeプロパティを持ち、更新の種類を区別します。

  • dispatch関数(ディスパッチ)

    アクションを送信して、状態の更新を実行するための関数です。

  • reducer関数(リデューサー)

    現在の状態とアクションを受け取り、新しい状態を返す関数です。状態の更新ロジックはここに集約されます。

これらの要素は、次の流れで状態を更新します。

  1. ユーザー操作などをきっかけに、dispatch関数がアクションを送信する
  2. reducer関数が現在の状態とアクションを受け取り、更新ルールに基づいて新しい状態を返す
  3. 新しい状態がコンポーネントに反映され、画面が再描画される

図:useReducerの状態更新の仕組み

reducer関数が新しい状態を返し、コンポーネントに反映され、画面が再描画される

useReducerの処理の流れをコード例で確認

useReducerの4つの要素が実際のコードでどのように動くのか、カウンターアプリを例に確認します。

以下は、カウントアップ機能をuseReducerを使用して記述したものです。

// カウントアップ機能 全体のコード

// useReducerをインポート
import { useReducer } from 'react';

// reducer関数の定義
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
}

// カウントアプリのコンポーネント
function Counter() {

  // useReducerフックの呼び出しとstateとdispatchを取得
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <div>
      <h1>Count : {state.count}</h1>

      {/* dispatch関数の定義 */}
      <button onClick={() => dispatch({ type: 'increment' })}>
        Increment
      </button>
      <button onClick={() => dispatch({ type: 'decrement' })}>
        Decrement
      </button>
    </div>
  );
}

export default Counter;
  1. useReducerのインポート

    はじめに、コンポーネントのトップレベルでuseReducerをインポートします。

    // useReducerをインポート
    import { useReducer } from 'react';
  2. Reducer関数の定義

    reducer関数は状態がどのように変化するかを決める関数です。function reducer(state, action)部分で現在の状態と、どのように変えたいかを示すアクションを受け取り、return state; で変更後の状態を返します。

    以下のコードでは、action.typeincrementの場合はcountが1つ増え、decrementの場合はcountが1つ減ります。switchを使用して、どのアクションに対してどのように状態を変えるかを定義します。(条件分岐はif文でも実装可能です)

    // Reducer関数
    function reducer(state, action) {
      switch (action.type) {
        case 'increment':
          return { count: state.count + 1 };
        case 'decrement':
          return { count: state.count - 1 };
        default:
          return state;
      }
    }
  3. useReducerの設定

    useReducerを呼び出して、状態の初期値を設定します。

    // useReducerフックの呼び出し
    const [state, dispatch] = useReducer(reducer, { count: 0 });
  4. dispatch関数を使用しアクションを送信

    状態を更新したいときはdispatch関数を使用し、アクションを渡すことで状態が更新されます。今回の例では、<button onClick={() => dispatch({ type: 'increment' })}>に記述されている、dispatch({ type: 'アクション名' })部分がdispatch関数です。

    ユーザーの操作(ボタンクリック)をトリガーにして、コンポーネントの現在の状態をどのように更新するかをアクションとしてReducer関数に渡します。

    // dispatch関数にアクションを渡す
    dispatch({ type: 'increment' }) // 'increment' アクションが渡される
    dispatch({ type: 'decrement' }) // 'decrement' アクションが渡される

useReducerとuseStateの比較:同じ機能をした場合

状態の管理や変更は、useStateを使っても実装できます。これまで説明してきたカウントアップ機能をuseStateで記述すると、以下のようになります。

// useStateのインポート
import { useState } from 'react';

// カウントアプリのコンポーネント
function Counter() {
  // useStateフックを使用して状態を管理
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>Count : {count}</h1>

      {/* カウントを増減させるためのボタン */}
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
      <button onClick={() => setCount(count - 1)}>
        Decrement
      </button>
    </div>
  );
}

export default Counter;

このように、useReducerを使用するよりもuseStateの方がコードはシンプルになります。今回のような単純なカウントアップ機能であれば、useStateを使う方が、実装が容易になり、コードの可読性も向上し、状態の管理がしやすくなります。

今回はuseReducerをより理解しやすくするために、単純なカウントアップ機能のコードを例に説明しましたが、実装する機能によってはuseStateの方が適している場合があります。

useStateとuseReducerの使い分け

useStateとuseReducerは、どちらもReactの状態管理に使えるHookですが、状態の複雑さや更新パターンの数によって向き不向きがあります。明確なルールがあるわけではなく、コードの見通しが良くなる方を選ぶのが実践的です。

使い分けの具体例

以下の表を目安に、状況に応じて使い分けるのがおすすめです。

判断基準useStateuseReducer
状態の構造単一の値(トグル、1つの入力フィールドなど)複数の関連する値をまとめて管理(ユーザー登録フォームなど)
更新パターン1〜2種類程度のシンプルな更新追加・削除・編集など複数の操作がある
更新ロジックsetStateで直接更新できるreducer関数に集約した方が見通しが良い

迷ったときは、まずuseStateで実装してみて、更新パターンが増えてコードが読みにくくなってきたタイミングでuseReducerへの移行を検討してみてください。

useReducerを使ってみよう

このセクションでは実際にコードを試して、useReducerの理解を深めていきます。このハンズオンでは、以下のTodoアプリを作成します。

ハンズオンを手軽に試したい方には、以下のサイトがおすすめです。Reactの環境構築が不要で、ブラウザ上でコードをすぐに試すことができます。

useReducerを使用したTodoアプリの作成

このハンズオンで作成するTodoアプリでは、useReducerを使ってタスクの追加と削除を管理し、useStateを使って入力されたタスクのテキストを保持します。

このハンズオンでは、以下のファイルを作成します。

  • App.jsx
  • TodoApp.jsx

各ファイルを編集します。

App.jsx

// App.jsx

import TodoApp from './TodoApp';
import './App.css';

function App() {
  return (
    <div>
      <h2>ToDo List</h2>
      <TodoApp />
    </div>
  );
}

export default App;

TodoApp.jsx

// TodoApp.jsx

import { useReducer, useState } from 'react';

// Reducer関数:アクションに応じてToDoリストの状態を更新
function todoReducer(todos, action) {
  switch (action.type) {
    case 'add':
      return [...todos, { id: Date.now(), text: action.text }];
    case 'delete':
      return todos.filter((todo) => todo.id !== action.id);
    default:
      throw new Error('Unknown action type');
  }
}

// TodoAppコンポーネント:ToDoリストのUIとロジックを管理
export default function TodoApp() {
  // useReducerでToDoリストの状態を管理 初期値は空のリスト
  const [todos, dispatch] = useReducer(todoReducer, []);

  // 入力されたテキストの状態を管理
  const [text, setText] = useState('');

  // タスクを追加する関数:テキストが空でない場合にdispatchを実行
  const handleAdd = () => {
    if (text.trim()) {
      dispatch({ type: 'add', text });
      setText('');
    }
  };

  return (
    <div>
      <input
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="タスクを入力してください"
      />
      <button onClick={handleAdd}>Add Task</button>
      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>
            {todo.text}

            {/* タスクを削除するためのボタン:クリックでdispatchを実行 */}
            <button onClick={() => dispatch({ type: 'delete', id: todo.id })}>
              Delete
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}

コードの編集後、フォームに入力したタスクが追加され、Deleteボタンを押すとタスクが削除されます。useReducerを使うことで、複数の状態やアクションを効率的に管理できるようになります。

useReducerに関するよくある質問

useReducerに関するよくある質問を、以下の通りにまとめました。

Q. useReducerとuseStateはどちらを使うべき?

状態の更新パターンが1〜2種類程度であればuseStateで十分です。更新パターンが増えてきた場合や、複数の状態をまとめて管理したい場合はuseReducerが適しています。明確なルールはないので、コードの見通しが良くなる方を選ぶのが実践的です。

Q. reducer関数の中で非同期処理はできる?

reducer関数は純粋関数である必要があるため、内部でAPI通信などの非同期処理は行えません。非同期処理はuseEffectなどで実行し、その結果をdispatchでreducerに渡す形にします。

Q. actionのtype名はどう決めればいい?

typeには、何をする操作かが伝わる名前をつけます。たとえばカウンターであれば「INCREMENT」「DECREMENT」、Todoアプリであれば「ADD_TODO」「DELETE_TODO」のように、大文字のスネークケースで記述するのが一般的です。

Q. useReducerと状態管理ライブラリは何が違う?

useReducerはReactに組み込まれたHookで、コンポーネント単位の状態管理に使います。ReduxやZustandなどの状態管理ライブラリは、アプリ全体でグローバルに状態を共有する仕組みを提供するもので、用途や規模が異なります。小〜中規模の状態管理であれば、useReducerで十分対応できるケースが多いです。

まとめ

この記事では、useReducerの仕組みとuseStateとの使い分けについて解説しました。

学んだ内容:

この記事の要点は以下の通りです。

  • useReducerは複数の状態更新を1つのreducer関数にまとめて管理できるHook
  • 状態・アクション・dispatch関数・reducer関数の4要素で構成
  • useStateとuseReducerの使い分けは、状況に応じた判断が大切

useReducerを身につけたことで、状態管理の選択肢はuseStateだけではなくなりました。今後、フォームやTodoリストのように複数の操作が絡む機能を実装する場面で、「useReducerで整理すればスッキリ書けそうだ」と判断できる自分に気づくはずです。その積み重ねが、React開発全体への自信につながっていきます。

参考資料

以下のリンクは、この記事で説明した手順や概念に関連する参考資料です。より詳しく学びたい方は、ぜひご参照ください。

【番外編】USBも知らなかった私が独学でプログラミングを勉強してGAFAに入社するまでの話

IT未経験者必見 USBも知らなかった私が独学でプログラミングを勉強してGAFAに入社するまでの話

プログラミング塾に半年通えば、一人前になれると思っているあなた。それ、勘違いですよ。「なぜ間違いなの?」「正しい勉強法とは何なの?」ITを学び始める全ての人に知って欲しい。そう思って書きました。是非読んでみてください。

「フリーランスエンジニア」

近年やっと世間に浸透した言葉だ。ひと昔まえ、終身雇用は当たり前で、大企業に就職することは一種のステータスだった。しかし、そんな時代も終わり「優秀な人材は転職する」ことが当たり前の時代となる。フリーランスエンジニアに高価値が付く現在、ネットを見ると「未経験でも年収400万以上」などと書いてある。これに釣られて、多くの人がフリーランスになろうとITの世界に入ってきている。私もその中の1人だ。数年前、USBも知らない状態からITの世界に没入し、そこから約2年間、毎日勉学を行なった。他人の何十倍も努力した。そして、企業研修やIT塾で数多くの受講生の指導経験も得た。そこで私は、伸びるエンジニアとそうでないエンジニアをたくさん見てきた。そして、稼げるエンジニア、稼げないエンジニアを見てきた。

「成功する人とそうでない人の違いは何か?」

私が出した答えは、「量産型エンジニアか否か」である。今のエンジニア市場には、量産型エンジニアが溢れている!!ここでの量産型エンジニアの定義は以下の通りである。

比較的簡単に学習可能なWebフレームワーク(WordPress, Rails)やPython等の知識はあるが、ITの基本概念を理解していないため、単調な作業しかこなすことができないエンジニアのこと。

多くの人がフリーランスエンジニアを目指す時代に中途半端な知識や技術力でこの世界に飛び込むと返って過酷な労働条件で働くことになる。そこで、エンジニアを目指すあなたがどう学習していくべきかを私の経験を交えて書こうと思った。続きはこちらから、、、、

note記事3000いいね超えの殿堂記事 LINE登録で記事を見る

エンベーダー編集部

エンベーダーは、ITスクールRareTECHのインフラ学習教材として誕生しました。 「遊びながらインフラエンジニアへ」をコンセプトに、インフラへの学習ハードルを下げるツールとして運営されています。

RareTECH 無料体験授業開催中! オンラインにて実施中! Top10%のエンジニアになる秘訣を伝授します! RareTECH講師への質疑応答可

関連記事

2026.03.07

初心者にもわかるReactのインストールと基礎知識を解説

Reactの開発環境の構築方法を初心者向けに解説。Node.jsの導入からViteを使ったプロジェクト作成、起動確認、基本概念まで手順に沿って学べます。

  • フロントエンド
  • ハンズオン
  • React
  • JavaScript

2024.03.24

JavaScriptって何?レベルの初心者がとりあえず触ってみるハンズオン

このハンズオンを通じて、プログラミング初心者やJavaScriptに苦手意識を持つ方でも、JavaScriptの使い方を基礎から簡単に学べます。特別な経験や知識は一切必要ありません。パソコンとインターネットの基本操作ができれば、準備は完了です。わからないことがあっても大丈夫。一旦はこういうものだと思って進めてみてください。

  • ハンズオン
  • JavaScript
  • フロントエンド

2024.08.17

Reactの基本、useEffectを理解しよう

フロントエンドエンジニアを目指す方や、初めてReactを学ぶ方に向けて、この記事ではReact HooksのuseEffectについて解説します。useEffectの仕組みは少し複雑で初学者の方にとって難しく感じるかもしれませんが、Reactでの開発では必須の技術となります。useEffectの基本を押さえておくことで、これからのReact学習の理解度が深まります。基本を学び、フロントエンドエンジニアへの道を進んでいきましょう!

  • プログラミング
  • React
  • フロントエンド

2024.01.30

【初心者向け】Next.jsで高速でSEOに強いWebアプリケーションを構築

この記事では、近年、モダンフロントエンド開発において最も人気の高いVercel社が提供しているオープンソースのWebアプリケーションフレームである、「Next.js」について解説します。

  • フロントエンド