0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【React入門】useReducerで状態管理を整理しよう!useContextとの組み合わせも解説

Last updated at Posted at 2025-10-22

🧭 はじめに

前回の記事では、useContext を使ったグローバルな状態共有について紹介しました。
しかし、アプリが大きくなるにつれて「状態更新のロジックが複雑になってきた…」と感じることはありませんか?

そんなときに活躍するのが useReducer です。
今回は、useContext とうまく連携しながら、よりわかりやすく状態を管理する方法を学びましょう。

🧩 前提条件

  • Reactの基本(useState, useEffect, useContext)を理解している

  • 環境:Vite または Create React App

  • JavaScriptベース(TypeScriptでも応用可)

🎯 今回の目標

useReducer の基本的な使い方を理解する

useContext と組み合わせてグローバル状態を管理できるようになる

⚙️ useReducerとは?

useReducer は、複雑な状態管理を整理するための useStateの上位互換 のようなフックです。
状態の更新処理を「reducer関数」として1か所にまとめられるため、ロジックが明確になります。

const [state, dispatch] = useReducer(reducer, initialState)

state:現在の状態

dispatch:状態を更新するための関数

reducer:状態をどう変えるかを定義した関数

🧠 基本構文サンプル

カウンターを例にしてみましょう。

App.jsx
import React, { useReducer } from "react";

const initialState = { count: 0 };

function reducer(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;
  }
}

export default function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div className="p-4 text-center">
      <h2>Count: {state.count}</h2>
      <div className="space-x-2 mt-2">
        <button onClick={() => dispatch({ type: "INCREMENT" })}></button>
        <button onClick={() => dispatch({ type: "DECREMENT" })}></button>
        <button onClick={() => dispatch({ type: "RESET" })}>リセット</button>
      </div>
    </div>
  );
}

✅ dispatch に「 どんな操作をしたいか(type) 」を送るだけで状態を更新できます。
✅ reducer 関数でロジックを一元管理できるため、コードがすっきりします。

🌐 useContextと組み合わせる

複数コンポーネントで同じ状態を使いたい場合、
useReducer と useContext を組み合わせることで「軽量なRedux」のように使えます。

1️⃣ Contextを作成

CounterContext.jsx
import React, { createContext, useReducer, useContext } from "react";

const CounterContext = createContext();

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case "INCREMENT":
      return { count: state.count + 1 };
    case "DECREMENT":
      return { count: state.count - 1 };
    default:
      return state;
  }
}

export function CounterProvider({ children }) {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <CounterContext.Provider value={{ state, dispatch }}>
      {children}
    </CounterContext.Provider>
  );
}

// カスタムフックでContextを使いやすくする
export const useCounter = () => useContext(CounterContext);

2️⃣ 子コンポーネントで使う

CounterComponents.jsx
import React from "react";
import { useCounter } from "./CounterContext";

export function CounterDisplay() {
  const { state } = useCounter();
  return <h2>Count: {state.count}</h2>;
}

export function CounterButtons() {
  const { dispatch } = useCounter();
  return (
    <div className="space-x-2">
      <button onClick={() => dispatch({ type: "INCREMENT" })}></button>
      <button onClick={() => dispatch({ type: "DECREMENT" })}></button>
    </div>
  );
}

3️⃣ Appでまとめる

App.jsx
import React from "react";
import { CounterProvider } from "./CounterContext";
import { CounterDisplay, CounterButtons } from "./CounterComponents";

export default function App() {
  return (
    <CounterProvider>
      <div className="p-4 text-center">
        <h1 className="text-xl font-bold mb-2">useReducer + useContext例</h1>
        <CounterDisplay />
        <CounterButtons />
      </div>
    </CounterProvider>
  );
}

✅ useReducer が状態管理ロジックを担当
✅ useContext がその状態を全コンポーネントで共有
👉 結果、シンプルでスケーラブルな構成になります!

💡 useStateとの違いまとめ

比較項目 useState useReducer
状態の更新 直接set関数で行う dispatch経由でactionを送る
更新ロジック 分散しやすい reducerに一元化できる
スケーラビリティ 小規模向け 中〜大規模向け

🚀 まとめ

  • useReducer は複雑な状態管理をシンプルに整理できる

  • useContext と組み合わせるとグローバル管理も簡単

  • Reduxより軽量で、React標準機能だけで完結できる

🏁 最後に

React Hooksを使いこなすポイントは、「目的に応じてフックを使い分けること」です。
useState で十分な場面もあれば、アプリが成長して useReducer + useContext の方が見通しが良くなる場合もあります。

小さなサンプルを通して、下記を意識して理解を深めましょう。

  • reducerのロジックをどこまで切り出すか
  • Contextをどこでラップするか

 最後までご覧いただきありがとうございます。今後も引き続き復習を兼ねて投稿していきたいと思います。
 他にも記事を投稿しておりますので、是非そちらもご覧ください。今後もよろしくお願いいたします。

🔖 関連記事

0
0
0

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?