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/itemAddedやcheckout/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必須とは限りません。
次の順で確認します。
- component compositionで中間propsを減らせないか
- state ownerを必要なcomponentへ近づけられないか
- Contextでstableな値を提供すれば十分か
- 更新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を使う判断フロー
次の順に判断します。
- そのstateは1 component内だけか → useState
- 近いcomponent間だけか → lift up・props
- 1 feature内で更新が複雑か → useReducer
- subtreeへdeepに共有したいだけか → Context
- URL・server cache・formが適切なownerではないか確認
- app全体の多数箇所で読み書きするmutable client stateか
- 更新が頻繁・複雑でaction履歴・middleware・team ruleが必要か
- 導入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を学ぶことを勧めています。
- component・props・render
- useStateとimmutable update
- stateのlift upとsingle source of truth
- useReducerでeventとstate transitionを分ける
- Contextでdeepに共有する
- ReduxのStore・Action・Reducer・Dispatch
- Redux ToolkitのconfigureStore・createSlice
- React-ReduxのProvider・useSelector・useDispatch
- 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より追跡可能性の利益が大きいか」を確認してください。