React

Reduxとは?Reactで使う意味・不要なケース・Redux Toolkitを解説

Reduxとは何か、Reactで使う意味を初心者向けに解説します。Reduxが不要・必要なケース、useState・useReducer・Contextとの違い、Redux Toolkit、RTK Query、Next.jsでの注意点まで整理します。

この記事の目次
  1. Reduxとは何か
  2. Reduxのdata flow
  3. Reduxを使う意味
  4. stateの所有場所を共通化できる
  5. 更新理由をactionとして記録できる
  6. 更新logicをcomponent外へ整理できる
  7. DevToolsでstate変化を追える
  8. すべてのReactアプリにReduxは必要ない
  9. Reduxが不要なケース
  10. 1 component内で完結するstate
  11. 近い親子・兄弟componentだけで共有するstate
  12. 1つのformの編集中state
  13. 小さな学習app・短命なprototype
  14. React標準機能で対応できる範囲
  15. ContextとReduxの違い
  16. props drillingだけでReduxを決めない
  17. stateの種類ごとに保存場所を決める
  18. Reduxが役立つケース
  19. 既存projectがReduxを標準化している
  20. Reduxのメリットとtrade-off
  21. 現在Reduxを使うならRedux Toolkitを選ぶ
  22. Redux Toolkitの最小構成
  23. Redux storeに入れるstateを選ぶ
  24. plain serializable valueを保存する
  25. derived valueをstoreへ重複保存しない
  26. API dataだけが理由ならserver stateを分けて考える
  27. Redux DevToolsを採用理由に含める
  28. Next.js App RouterでReduxを使う注意点
  29. 古いRedux記事から読み替えるポイント
  30. Reduxを使う判断フロー
  31. Reduxでよくある誤解
  32. Reactを使うならReduxも必須
  33. Reduxへすべてのstateを入れる
  34. ContextがあればReduxは完全に不要
  35. Reduxを入れればperformanceが上がる
  36. createSlice reducerはstateを直接mutationしている
  37. Reduxはもう古くて使えない
  38. Reduxを学ぶ順番
  39. Redux採用チェックリスト
  40. まとめ

Reduxとは、JavaScript applicationの共有stateを予測可能な手順で管理するためのlibraryです。Reactの標準機能でも必須packageでもなく、React以外でも利用できます。

Reactで使う場合は、通常次の3つを組み合わせます。

  • Redux: store・action・reducerという状態管理の仕組み
  • Redux Toolkit: Redux logicを書く現在の公式推奨方法
  • React-Redux: React componentとRedux storeを接続するbinding

すべてのReact applicationにReduxが必要なわけではありません。component内で完結するstateはuseState、複雑なlocal更新はuseReducer、tree内のdeepな共有はContextで足りる場合があります。複数画面で共有するclient stateが多く、更新が頻繁・複雑で、action履歴を追いたい場合にReduxを検討します。

この記事では「Reduxの意味」「なぜ使うか」「不要・必要なケース」「Redux Toolkitとの関係」を、現行のReact・Redux公式方針に沿って解説します。

Reduxとは何か

Reduxは、application全体で共有するstateと、そのstateがどのeventでどう変わるかを一定のdata flowへ整理します。

Reduxの中心となる要素は次のとおりです。

用語 意味
Store Redux stateを保持する場所 cart・filter・workflow
State 現在のapplication data { cart: { items: [] } }
Action 何が起きたかを表すplain object cart/itemAdded
Dispatch actionをstoreへ送る操作 dispatch(itemAdded(item))
Reducer 現在stateとactionから次stateを計算 cartへitemを追加
Selector stateから必要な値を読み取る関数 cart item数を計算

Reduxはstateを「どこからでも直接書き換える」のではなく、eventをactionとしてdispatchし、reducerで次stateを計算します。これにより「いつ、どのactionで、どのstateが変わったか」を追いやすくします。

Reduxのdata flow

ReactとReduxを組み合わせた基本flowは一方向です。

ユーザー操作・API結果
        ↓
Actionをdispatch
        ↓
Reducerが次のStateを計算
        ↓
StoreのStateが更新
        ↓
useSelectorで読むComponentが再render

componentがstore stateを読むにはReact-ReduxのuseSelector()、actionを送るにはuseDispatch()を使います。

Reduxは「状態を保存する箱」だけではありません。action・reducer・middleware・DevToolsを含むdata flowに価値があります。仕組みを詳しく追う場合はReduxのStore・Action・Reducerの流れを参照してください。

Reduxを使う意味

Reduxが解決しようとする中心課題は、共有stateの更新を予測可能にすることです。

stateの所有場所を共通化できる

同じclient stateを複数画面・複数componentが読み書きする場合、各componentへcopyすると同期ずれが起きます。Redux storeへ原本を置き、必要な場所がselectorで読む構成にできます。

更新理由をactionとして記録できる

cart/itemAddedcheckout/submittedのように、何が起きたかをaction名で表せます。単なるsetter名ではなくdomain eventとして設計すると、履歴を読みやすくできます。

更新logicをcomponent外へ整理できる

reducerへstate transitionを集めることで、UI event handlerから複雑な更新logicを分離できます。reducerは同じstate・actionから同じ結果を返し、副作用を持たないようにします。

DevToolsでstate変化を追える

Redux DevToolsではdispatchされたaction、action内容、更新後state、state差分、dispatch元traceなどを確認できます。複雑な更新をteamで追跡する価値がある場合に強みになります。

すべてのReactアプリにReduxは必要ない

Redux公式FAQも「すべてのappにReduxが必要ではない」と説明しています。Reduxを入れると次が増えます。

  • Store・Action・Reducer・Selectorなどの概念
  • Provider・slice・hookなどの設定
  • stateをserializable・immutableに扱う制約
  • 直接更新せずdispatchを経由するindirection
  • team内で守る設計rule

小さなappでlocal stateだけなら、このcostが利益を上回ります。Reduxはcodeを最短にするtoolではなく、長期的に共有stateの変更を追跡しやすくするtoolです。

Reduxが不要なケース

1 component内で完結するstate

modal開閉、tab、input、hover、一時的なselectionなど、そのcomponentだけが使うstateはuseStateへ置きます。

import { useState } from "react";

function Disclosure() {
  const [open, setOpen] = useState(false);

  return (
    <>
      <button onClick={() => setOpen((value) => !value)}>
        詳細
      </button>
      {open && <p>説明文</p>}
    </>
  );
}

Reduxへ移す理由はありません。useStateの更新方法はReact useStateの使い方で解説しています。

近い親子・兄弟componentだけで共有するstate

2つのcomponentを同期するなら、React公式が説明する「stateを共通parentへliftする」方法から始めます。Redux storeを作る前に、適切なowner componentへstateを置き、propsで渡せないか確認します。

1つのformの編集中state

Redux公式style guideは、most form stateをlocalに保ち、userが編集を完了した時点で必要な値だけdispatchする方法を推奨しています。1文字入力するたびstoreへactionを送る価値がなければlocal stateにします。

小さな学習app・短命なprototype

counter、1画面のTodo、単純なformなどではReact標準機能で十分です。Redux導入が目的の学習課題でない限り、business problemよりtool設定が大きくならないようにします。

React標準機能で対応できる範囲

Reduxの前に、stateのscopeへ合うReact標準機能を選びます。

方法 向く場面 特徴
useState localで単純なstate 最小構成
stateのlift up 近いcomponent間の同期 共通parentをsource of truthにする
useReducer 1画面・feature内の複雑な更新 更新logicをreducerへ集約
Context tree内のdeepな値共有 propsを各階層へ渡さず提供
useReducer + Context subtree全体のstateとdispatch共有 外部libraryなしで拡張

React公式はuseReducerとContextを組み合わせ、deepなcomponentからstateを読み、actionをdispatchする構成も案内しています。詳しくはuseReducerの使い方useContextの使い方を参照してください。

ContextとReduxの違い

Contextは、parentから下のtreeへ値を明示的なpropsなしで渡すReact機能です。Context自体がstate transition、middleware、action log、DevToolsを提供するわけではありません。

比較 Context + reducer Redux Toolkit
追加package 不要 必要
共有範囲 Provider subtree Provider subtree
更新logic 自分でreducer設計 createSliceなどを利用
middleware 自作 標準構成あり
action履歴・DevTools 標準ではない configureStoreで接続
selector・subscription Context value単位 useSelectorでsliceを購読

「deepに値を渡したい」だけならContextで解決できます。多数のshared state、複雑なevent、async workflow、action traceなど複数の課題がある場合にReduxの価値が増えます。

props drillingだけでReduxを決めない

途中のcomponentが使わないpropsを何層も渡すprops drillingは不便ですが、それだけでRedux必須とは限りません。

次の順で確認します。

  1. component compositionで中間propsを減らせないか
  2. state ownerを必要なcomponentへ近づけられないか
  3. Contextでstableな値を提供すれば十分か
  4. 更新logic・頻度・共有範囲・debug要件も複雑か

4まで該当し、local architectureでは追跡が難しい場合にReduxを比較します。

stateの種類ごとに保存場所を決める

「globalに見える」という理由だけで全部Reduxへ入れず、stateの種類を分けます。

stateの種類 最初に検討する場所
Local UI state modal、tab、input useState
Feature state editor workflow、multi-step操作 useReducer、Context、Redux
Shared client state cart、cross-page draft、complex filter Reduxを含むshared state solution
URL state search query、sort、page、route URL search params・router
Server state API response・cache・loading framework・data cache、Redux appならRTK Query
Form draft 入力中のfield・validation local state・form solution

URLへ置くべきfilterをReduxだけへ保存するとbookmark・share・browser backが不自然になります。server dataをReduxへcopyするとsource of truthが増える場合があります。stateの性質に合うownerを選びます。

Reduxが役立つケース

Redux公式FAQは、次の条件で特に有用と説明しています。

  • appの多くの場所で必要なapplication stateが大量にある
  • stateが頻繁に更新される
  • state更新logicが複雑
  • 中規模・大規模codebaseを複数人で扱う
  • stateが時間とともにどう更新されたか確認したい

たとえば複数画面にまたがるcart、権限で分岐する長いclient workflow、offline queue、undo / redoが必要なeditorなどです。ただし名前だけで判断せず、実際の共有範囲・更新数・debug要件を確認します。

既存projectがReduxを標準化している

teamがRedux Toolkitのslice、selector、async logic、test patternを共有している場合、新featureも同じarchitectureへ揃える長期的価値があります。逆に小さなprojectへ「有名だから」だけで導入しません。

Reduxのメリットとtrade-off

メリット trade-off
shared stateのsourceを整理 storeへのindirectionが増える
actionで変更理由を表現 action・slice設計が必要
reducerをtestしやすい pure update ruleを学ぶ必要
DevToolsで履歴・diffを追跡 serializable stateなど制約がある
middlewareでasync・side effectを整理 architecture選択が増える
teamで共通patternを持てる 小規模appではoverheadになる

短期的なcode量ではuseStateが少なくても、長期的な変更追跡ではReduxが有利になる場合があります。どちらのcostを解決したいかで判断します。

現在Reduxを使うならRedux Toolkitを選ぶ

Redux Toolkit(RTK)はRedux logicを書く公式推奨toolsetです。新規codeでlegacyなredux core packageだけを直接使う方法ではなく、@reduxjs/toolkitを使います。

主なAPIは次のとおりです。

API 役割
configureStore store、middleware、DevToolsを適切なdefaultで設定
createSlice reducer・action creator・action typeをfeature単位で作成
createAsyncThunk Promise lifecycle actionを生成
createEntityAdapter normalized entity stateのCRUDを補助
createSelector memoized derived data
RTK Query data fetching・cacheを管理

Redux ToolkitもReduxです。store・action・reducer・dispatchという基本flowは同じで、boilerplateと典型的なmistakeを減らします。

Redux Toolkitの最小構成

まずpackageをinstallします。

npm install @reduxjs/toolkit react-redux

counter sliceを作ります。

// features/counter/counterSlice.js
import { createSlice } from "@reduxjs/toolkit";

const counterSlice = createSlice({
  name: "counter",
  initialState: { value: 0 },
  reducers: {
    increment(state) {
      state.value += 1;
    },
    decrement(state) {
      state.value -= 1;
    },
  },
});

export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;

見た目はstateを直接変更していますが、createSliceはImmerのdraftを使い、immutableな次stateを生成します。Redux Toolkit外でstore stateを直接mutationしてよいという意味ではありません。

storeへslice reducerを登録します。

// app/store.js
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "../features/counter/counterSlice";

export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
});

React rootをProviderで囲みます。

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { Provider } from "react-redux";
import { store } from "./app/store";
import App from "./App";

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </StrictMode>
);

componentからstateを読み、actionをdispatchします。

import { useDispatch, useSelector } from "react-redux";
import { increment } from "./counterSlice";

export function Counter() {
  const value = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();

  return (
    <button onClick={() => dispatch(increment())}>
      {value}
    </button>
  );
}

この最小例の目的は関係性の確認です。feature構成、TypeScript、DevToolsを含む実装はRedux Toolkitの始め方へ分離しています。

Redux storeに入れるstateを選ぶ

Reduxを採用しても、すべてのstateをstoreへ入れる必要はありません。公式FAQは次の問いを判断材料にしています。

  • appのほかの場所がこのdataを必要とするか
  • 同じdataが複数componentを動かすか
  • 元dataからderived dataを作る必要があるか
  • 過去stateへ戻す・変化を追う価値があるか
  • cacheして再利用する価値があるか

local dropdownのopen状態はuseState、cross-page cartはstore候補というように、stateごとに配置を決めます。

plain serializable valueを保存する

Redux stateとactionには、plain object・array・string・number・boolean・nullなどserializableな値を使います。

次のような値は原則storeへ入れません。

  • function・Promise
  • DOM node・React element
  • class instance
  • Map・Setなど、そのままserializationできない値

non-serializable valueはpersist・rehydration・time travel・DevTools・UI updateを壊す原因になります。必要なIDやprimitive dataだけを保存します。

derived valueをstoreへ重複保存しない

元stateから計算できるfiltered list・合計・件数を別stateとして保存すると、元dataとの同期が必要になります。

const selectCompletedTodos = (state) =>
  state.todos.items.filter((todo) => todo.completed);

const selectCompletedCount = (state) =>
  selectCompletedTodos(state).length;

storeには元dataをminimalに保持し、selectorで必要な形へderiveします。重い計算を複数箇所で使う場合はcreateSelectorによるmemoizationを検討します。

API dataだけが理由ならserver stateを分けて考える

API responseにはfetching・cache・deduplication・refetch・stale判定というclient stateとは異なる課題があります。

既にReduxを使うappでは、Redux公式はRTK Queryをdata fetchingとcacheのdefault approachとして教えています。endpointを定義するとquery hook、loading state、cache、request deduplicationなどを管理できます。

一方、framework・router・data cacheがserver dataを管理できるappで、共有client stateの課題がないなら、fetchのためだけにReduxを導入する必要はありません。「client state」と「server state」を同じ問題として扱わないことが重要です。

Redux DevToolsを採用理由に含める

Redux ToolkitのconfigureStoreはRedux DevTools Extensionへ接続するdefault設定を持ちます。確認できる代表例は次のとおりです。

  • dispatchされたactionの時系列
  • action type・payload
  • action前後のstate diff
  • 最終state
  • dispatch元のstack trace

「なぜこの画面状態になったか」を複数人で再現・調査する必要があるprojectでは大きな価値があります。小さなcomponent local stateにはこの仕組みが不要なこともあります。

Next.js App RouterでReduxを使う注意点

Next.js App RouterはSPAと同じstore設計をそのまま使えません。Redux公式の2026年更新guideでは次を推奨しています。

  • module globalのsingleton storeをserverで共有しない
  • storeをrequestごとに作成する
  • storeの作成・Provider・read・writeはClient Componentで行う
  • React Server ComponentからRedux storeを操作しない
  • Reduxはglobalでmutableなdataへ限定する
  • search params・route params・form stateなどはNext.js・React側を使う

server request間でstoreを共有すると別user dataが混ざる危険があります。Next.js projectではSPA用tutorialのexport const store = ...をそのままserver singletonとして置かず、公式App Router guideに沿ってper-request factoryとStoreProviderを設計します。

古いRedux記事から読み替えるポイント

古いtutorialのcodeはconcept理解には使えても、新規実装の推奨形ではない場合があります。

legacy pattern modern Redux
redux coreのcreateStore Redux ToolkitのconfigureStore
action type constantを手書き createSliceが生成
action creatorを手書き createSliceが生成
spreadで複雑なimmutable更新 Immer draft syntax
actions・reducers別folder feature folderとslice file
connect中心 function componentではhooks APIを推奨
fetch reducerを毎回手書き RTK Queryをdefault候補

legacy Redux appはすぐ動かなくなるわけではありません。段階的にcreateStoreからconfigureStore、new featureからcreateSliceへ移行できます。

Reduxを使う判断フロー

次の順に判断します。

  1. そのstateは1 component内だけか → useState
  2. 近いcomponent間だけか → lift up・props
  3. 1 feature内で更新が複雑か → useReducer
  4. subtreeへdeepに共有したいだけか → Context
  5. URL・server cache・formが適切なownerではないか確認
  6. app全体の多数箇所で読み書きするmutable client stateか
  7. 更新が頻繁・複雑でaction履歴・middleware・team ruleが必要か
  8. 導入costより長期保守の利益が大きいか

6〜8が強く該当すればRedux Toolkitを比較します。必要性が曖昧な段階では、React標準機能で問題を具体化してから導入できます。

Reduxでよくある誤解

Reactを使うならReduxも必須

誤りです。Reduxは独立libraryで、React appはReduxなしで動きます。React公式もstateのlift up、reducer、Contextによる管理方法を提供しています。

Reduxへすべてのstateを入れる

不要です。Redux採用後もlocal UI state・form draftはcomponentへ置けます。stateごとのownerを選びます。

ContextがあればReduxは完全に不要

Contextは値のtransportを解決しますが、middleware、selector subscription、action history、Redux DevToolsなどは別途設計が必要です。要求がContextの範囲ならRedux不要、要求が広ければ比較します。

Reduxを入れればperformanceが上がる

自動ではありません。useSelectorが返す値、state normalization、selector memoization、component構成により再renderは変わります。不必要なglobal stateや毎回new objectを返すselectorは逆に問題を増やします。

createSlice reducerはstateを直接mutationしている

createSlice内のstate.value += 1はImmer draftへの操作です。Immerがimmutable resultを生成します。storeから取得したstateをcomponentや通常functionで直接変更してはいけません。

Reduxはもう古くて使えない

Reduxは現在もmaintainされ、公式はRedux Toolkitによるmodern patternを推奨しています。「すべてのappに必要ではない」と「obsoleteである」は別です。課題とtrade-offで採用します。

Reduxを学ぶ順番

Redux公式は、new learnerがまずReactへ慣れてからReduxを学ぶことを勧めています。

  1. component・props・render
  2. useStateとimmutable update
  3. stateのlift upとsingle source of truth
  4. useReducerでeventとstate transitionを分ける
  5. Contextでdeepに共有する
  6. ReduxのStore・Action・Reducer・Dispatch
  7. Redux ToolkitのconfigureStore・createSlice
  8. React-ReduxのProvider・useSelector・useDispatch
  9. selector・async logic・RTK Query・DevTools

先にvanilla Reactでstate managementの痛点を経験すると、Reduxが追加する価値とcostを理解しやすくなります。

Redux採用チェックリスト

  • Redux・Redux Toolkit・React-Reduxの役割を区別した
  • local UI・URL・server・form・shared client stateを分けた
  • useState・lift up・useReducer・Contextで十分か確認した
  • props drillingだけを理由に採用していない
  • 多くの場所で共有するmutable stateがある
  • state更新が頻繁・複雑である
  • action履歴・DevTools・middlewareに価値がある
  • teamがslice・selector・async logicのruleを共有できる
  • 新規実装はRedux Toolkitを使う
  • store stateをminimal・serializableに保つ
  • derived dataをselectorで計算する
  • form draftを無条件にReduxへ入れていない
  • Redux appのAPI cacheではRTK Queryを比較した
  • Next.jsではper-request store・Client Component境界を設計した

まとめ

Reduxとは、shared application stateをsingle storeへ置き、actionをdispatchし、reducerで次stateを計算することで、変更を予測・追跡しやすくするJavaScript libraryです。ReactではReact-Reduxで接続し、現在の新規実装はRedux Toolkitを使います。

小さなapp、local UI、1つのform、近いcomponent間の共有にはReduxが不要なことが多く、useState・useReducer・Contextで対応できます。多数の画面で共有するstateが頻繁・複雑に変わり、DevTools・middleware・team共通ruleが必要な場合にReduxの価値が高まります。

Reduxを使うか迷ったら「そのstateの本来のownerはどこか」「何がReact標準機能ではつらいのか」「導入costより追跡可能性の利益が大きいか」を確認してください。