カテゴリ: React 更新日: 2026/04/02

React useReducerとContext APIを組み合わせた本格的な状態管理ガイド

useReducerとContextを組み合わせた状態管理
useReducerとContextを組み合わせた状態管理

先生と生徒の会話形式で理解しよう

生徒

「Reactでアプリが大きくなってくると、データの管理がバラバラになって大変です。もっとまとめて管理する方法はありませんか?」

先生

「そんな時こそ、ユーズリデューサー(useReducer)とコンテキストエーピーアイ(Context API)の出番です。これらを組み合わせると、アプリ全体のデータを一つの司令塔で管理できるようになりますよ。」

生徒

「司令塔ですか!難しそうですが、初心者でも使いこなせますか?」

先生

「仕組みはレストランの注文に似ています。まずは基本的な考え方から順番に、丁寧に解説していきますね!」

1. useReducerとContext APIを組み合わせる理由

1. useReducerとContext APIを組み合わせる理由
1. useReducerとContext APIを組み合わせる理由

Reactで小さなアプリを作る時は、useState(ユーズステート)という機能でデータを管理すれば十分です。しかし、本格的なアプリになると、「どこでデータが変わったのかわからない」「あちこちの部品にデータを渡すのが面倒」といった問題が発生します。

そこで、データの変更ルールを専門に扱う「useReducer」と、データをアプリ全体に配信する「Context API」を組み合わせます。これにより、まるでテレビの放送局のように、一箇所で管理している最新のデータを、必要な部品だけがいつでも受信できるようになります。この組み合わせは、プロの開発現場でも非常に多く使われる、効率的な状態管理の王道パターンです。

2. リデューサー(reducer)の役割を例え話で理解する

2. リデューサー(reducer)の役割を例え話で理解する
2. リデューサー(reducer)の役割を例え話で理解する

まずは「useReducer」の仕組みを理解しましょう。リデューサーは、例えるなら「レストランの厨房(ちゅうぼう)」です。厨房には、お客さん(画面の部品)から「注文票(アクション)」が届きます。

注文票には「ハンバーグを一つ追加」や「サラダをキャンセル」といった具体的な指示が書かれています。料理人(リデューサー)は、現在の在庫(今のデータ)を確認し、注文票の通りに新しい料理(新しいデータ)を作ります。このように、データの変え方を一箇所にまとめることで、勝手にデータが書き換わるミスを防ぐことができます。プログラミングにおいて、この注文を出す行為を「ディスパッチ(dispatch)」と呼びます。

3. ステップ1:リデューサー関数を作ってみよう

3. ステップ1:リデューサー関数を作ってみよう
3. ステップ1:リデューサー関数を作ってみよう

まずは、データの変更ルールを書いたリデューサー関数を作成します。ここでは、簡単なカウンターアプリを例にします。数字を増やす、減らす、ゼロに戻すといったルールを記述します。


// 現在のデータ(state)と、指示書(action)を受け取ります
function counterReducer(state, action) {
  switch (action.type) {
    case "INCREMENT":
      return { count: state.count + 1 };
    case "DECREMENT":
      return { count: state.count - 1 };
    case "RESET":
      return { count: 0 };
    default:
      return state;
  }
}
(action.type という指示の内容によって、次の数字がどうなるかを計算して返しています。これが「データの設計図」になります。)

4. ステップ2:コンテキスト(データの放送局)を準備する

4. ステップ2:コンテキスト(データの放送局)を準備する
4. ステップ2:コンテキスト(データの放送局)を準備する

次に、作ったデータを配るための「コンテキスト」を作成します。Reactの createContext(クリエイトコンテキスト)を使います。これを使うことで、深い階層にある部品まで、バケツリレーをせずにデータを届けることができます。


import React, { createContext, useReducer } from "react";

// データを配るための箱を二つ作ります(値用と、命令用)
export const StateContext = createContext();
export const DispatchContext = createContext();

export function CounterProvider({ children }) {
  const [state, dispatch] = useReducer(counterReducer, { count: 0 });

  return (
    <StateContext.Provider value={state}>
      <DispatchContext.Provider value={dispatch}>
        {children}
      </DispatchContext.Provider>
    </StateContext.Provider>
  );
}
(Provider(プロバイダー)というもので包むことで、その中にある全ての部品がデータを受け取れるようになります。)

5. ステップ3:部品からデータを呼び出す

5. ステップ3:部品からデータを呼び出す
5. ステップ3:部品からデータを呼び出す

準備ができたら、実際に画面に数字を表示する部品を作ります。ここで useContext(ユーズコンテキスト)という機能を使います。これを使えば、放送局から流れている最新のデータをキャッチできます。


import React, { useContext } from "react";
import { StateContext } from "./App";

function CounterDisplay() {
  // 放送局から現在のデータ(state)を受け取ります
  const state = useContext(StateContext);

  return (
    <div className="text-center p-3">
      <h2 className="display-4">現在の数値:{state.count}</h2>
    </div>
  );
}
(画面には、現在の count の値が表示されます。この部品は、データの変更ルールを知らなくても、ただ表示するだけで済みます。)

6. ステップ4:注文(ディスパッチ)を出してデータを更新する

6. ステップ4:注文(ディスパッチ)を出してデータを更新する
6. ステップ4:注文(ディスパッチ)を出してデータを更新する

最後に、データを変更するためのボタンを作ります。ここでは、先ほど準備した dispatch(ディスパッチ)という機能を使います。これに「INCREMENT(増やして!)」という指示を載せて送ることで、リデューサーが動き出します。


import React, { useContext } from "react";
import { DispatchContext } from "./App";

function CounterButtons() {
  // 命令を送るための dispatch を受け取ります
  const dispatch = useContext(DispatchContext);

  return (
    <div className="d-flex justify-content-center gap-2">
      <button 
        className="btn btn-primary"
        onClick={() => dispatch({ type: "INCREMENT" })}
      >
        増やす
      </button>
      <button 
        className="btn btn-danger"
        onClick={() => dispatch({ type: "DECREMENT" })}
      >
        減らす
      </button>
    </div>
  );
}
(ボタンを押すと、指示書がリデューサーに送られ、アプリ全体の数字が更新されます。数字が変わると、表示している部品も自動的に書き換わります。)

7. この組み合わせを使うメリット(パフォーマンスと保守性)

7. この組み合わせを使うメリット(パフォーマンスと保守性)
7. この組み合わせを使うメリット(パフォーマンスと保守性)

なぜこのように面倒な手順を踏むのでしょうか。それは、アプリが成長した時に「誰がいつデータを変えたのか」をはっきりさせるためです。全ての変更がリデューサーを通るため、デバッグ(不具合探し)が非常に楽になります。

また、Context APIを二つ(データ用と命令用)に分けることで、無駄な再描画を防ぐことができます。数字が変わった時に、数字を表示している部品だけが動き、ボタン部品は動かないように設定できるのです。パソコンを触ったことがない方でも、「役割分担をしっかりすることで、無駄な電気を使わずにサクサク動く」とイメージしていただければ正解です。

8. 実践的な活用シーン:買い物かごやログイン管理

8. 実践的な活用シーン:買い物かごやログイン管理
8. 実践的な活用シーン:買い物かごやログイン管理

この仕組みは、ショッピングサイトの「買い物かご」や、ユーザーがログインしているかどうかの「ログイン状態管理」で特によく使われます。買い物かごは、商品ページ、カート画面、ヘッダーの合計金額など、たくさんの場所で同じデータを使いますよね。

今回学んだ「useReducer + Context API」を使えば、商品をカゴに入れるという一箇所のアクションだけで、サイト中の全ての金額表示を正しく更新できます。一見難しく感じるコードも、一つ一つの部品の役割(料理人、注文票、放送局、受信機)を整理すれば、必ず理解できるようになります。少しずつ自分でもプログラムを書いて、動く楽しさを体験してみてください。

カテゴリの一覧へ
新着記事
New1
React
React useReducerとContext APIを組み合わせた本格的な状態管理ガイド
New2
React
ReactでPropsとStateを組み合わせてTodoアプリを作ろう!初心者でもできるReact入門
New3
React
Reactの条件分岐の使い方を完全ガイド!初心者でもわかるReactの条件分岐
New4
Next.js
Next.jsのCSRとSSRの違いを初心者向けにわかりやすく解説!Next.jsでのレンダリング比較ガイド
人気記事
No.1
Java&Spring記事人気No1
React
Reactとは?初心者でもわかるReact.jsの基本概念と特徴をやさしく解説
No.2
Java&Spring記事人気No2
React
Reactの子コンポーネントから親コンポーネントへデータを渡す方法を徹底解説!初心者にもわかるReactのイベントとデータの流れ
No.3
Java&Spring記事人気No3
React
ReactとTypeScriptの環境構築をやさしく解説!Viteとtsconfigの設定も丁寧に紹介
No.4
Java&Spring記事人気No4
React
Reactの学習ロードマップ!初心者が最短で習得する流れを完全ガイド
No.5
Java&Spring記事人気No5
React
ReactのContext APIでログイン状態を管理する方法を完全解説!初心者でもわかる認証機能の実装
No.6
Java&Spring記事人気No6
React
React Context APIの値を更新する方法!useStateと組み合わせてデータを動かす初心者ガイド
No.7
Java&Spring記事人気No7
React
React開発におすすめのVSCode拡張機能まとめ!初心者でもすぐ使える便利ツール紹介
No.8
Java&Spring記事人気No8
React
ReactのState管理ベストプラクティス!初心者でもわかる再レンダリング最適化の考え方