こんにちは!Reactの学習、毎日お疲れ様です。
「よし、useStateを使ってカウンターアプリが作れるようになったぞ!」と意気込んだのも束の間、
「あれ?なんか状態が複雑になりすぎて、どこで値を更新してるのか分かんなくなっちゃった……」
「入力フォームと、ローディング状態と、エラーメッセージが絡み合って、コードがスパゲッティ状態だよぅ……」
そんな壁にぶつかって、頭を抱えていませんか?大丈夫ですよ、まったく心配いりません。それは誰もが通る「React中級者への登竜門」であり、あなたがしっかりとコードに向き合っている証拠ですから。
`useState`はすごく便利で直感的なんですが、管理するデータが増えてくると、「誰が、いつ、どんな理由でそのデータを書き換えたのか」が追いにくくなってしまうという弱点があります。
そこで登場するのが、今回主役としてご紹介する `useReducer`(ユースリデューサー) です!
「名前からして難しそう……」なんて身構えなくて大丈夫。身近な例えを交えながら、優しく紐解いていきましょう。
—
1. `useReducer`ってなに?(お買い物の受付係で例えてみる)
`useReducer`をイメージしやすくするために、身近なシチュエーションを考えてみましょう。
例えば、あなたが大好きなカフェの「オーダーカウンター」の店員さんだとします。
お客さんがやってきて、こう言います。
- 「カフェラテを1つ追加して!」
- 「あ、やっぱホットじゃなくてアイスで!」
- 「やっぱり注文全部キャンセルで!」
お客さんは直接勝手にレジの数字をいじったりはしませんよね。必ず「こういう注文(リクエスト)をしたいんです」という指示書(=アクション)をあなた(店員さん)に渡します。
店員さんであるあなた(=reducer関数)は、その指示書を受け取ると、
「今の注文リストはこうだから、この指示を反映させると…新しい注文リストはこうなるな!」
と、頭の中で計算して、正確に伝票を書き換えます。
この仕組みをそっくりそのままReactに持ち込んだのが、`useReducer`なんです。
- 状態(State): いま現在のデータ(注文リスト)
- アクション(Action): 「こういう風に変えてほしい!」というお願い(指示書)
- ディスパッチ(dispatch): 指示書をポストに投函する、あるいは店員さんに「これお願い!」と渡すアクションそのもの
- リデューサー(Reducer): 指示書を受け取って、実際にデータをどう書き換えるかを決める「職人さん(計算係)」
`useState`が「直接データを書き換えるボタン」だとすれば、`useReducer`は「しっかりとしたルールの下で、手続きを踏んでデータを安全に更新する仕組み」なんです。
—
2. 基本の構文を見てみよう
百聞は一見に如かず。まずは基本の形を眺めてみましょう。
呪文のように見えますが、分解すれば怖くありません。
import React, { 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 (
現在のカウント: {state.count}
{/ ③ dispatchを使って、reducerに「指示書」を投げる /}
);
}
どうでしょう?少しスッキリして見えませんか?
ポイントは、コンポーネントの中身(画面を描く場所)が「ボタンが押されたら、こういう内容の指示書(`{ type: ‘INCREMENT’ }`)をdispatchする」というシンプルな記述だけに絞られている点です。
実際の複雑な計算やロジックは、コンポーネントの外にある `reducer` 関数がぜーんぶ引き受けてくれます。おかげでコードの見通しが劇的に良くなるんです。
—
3. dispatch(ディスパッチ)の役割と「指示書」の正体
ここで、先ほどから何度も登場している `dispatch` について、もう少し深く掘り下げてみましょう。
初心者の頃、「`dispatch`って結局なにをする魔法の言葉なの?」って思いがちですが、実はすごくシンプルなやつです。
`dispatch` は、私たちがReactに対して「おい!状態を変えてくれ!」と叫ぶためのメガホンです。
そして、そのメガホンに向かって叫ぶ内容(引数に渡すオブジェクト)が、いわゆる「指示書(アクション)」になります。
// 「カウントを2増やしてほしい!」という具体的な指示書をdispatchに託す
dispatch({ type: ‘INCREMENT_BY’, payload: 2 });
このとき、渡すオブジェクトの形には、業界でちょっとしたお約束(お作法)があります。
- `type`: 「なにをしたいのか」を表す名前(大文字で書くのが慣習です)
- `payload`(ペイロード): その作業に付随するデータ(今回の例なら「いくつ増やすか」の `2` など)※名前は自由ですが、一般的に `payload` や `value` と呼ばれます。
reducer関数はこの `type` を `switch` 文などで受け取り、
「なるほど、`type` が `’INCREMENT_BY’` だから、`payload` の分だけ足せばいいんだな!」と判断して、新しい状態を返すわけです。
役割分担を綺麗に整理すると、こうなります。
1. 画面(UI): ユーザーの操作を受けて、`dispatch({ type: ‘〇〇’ })` を呼ぶだけ。
2. dispatch: その指示書を `reducer` に届ける。
3. reducer: 指示書の内容を読み解き、安全に新しい状態を作り出して返す。
この「お仕事の分業」ができるようになると、アプリがどんなに大きくなっても頭がパニックにならなくなります。これが`useReducer`を導入する最大の旨味です。
—
4. 実用的なサンプルコード(ショッピングカートの例)
せっかくなので、もう少し実務に近い「お買い物の数量変更」をテーマにしたサンプルコードを書いてみましょう。
エディタにコピペして、ぜひ動きを確認してみてくださいね。
import React, { useReducer } from ‘react’;
// 1. 初期状態の定義
const initialState = {
item: ‘美味しいコーヒー豆’,
quantity: 1,
pricePerItem: 1000,
};
// 2. reducer関数(状態の更新ルールをすべてここに集約する)
function shoppingReducer(state, action) {
switch (action.type) {
case ‘ADD_QUANTITY’:
return {
…state, // スプレッド構文で、他のプロパティ(名前や単価)を維持する
quantity: state.quantity + 1,
};
case ‘SUB_QUANTITY’:
// 数量が0未満にならないように優しくガード
if (state.quantity <= 1) return state;
return {
...state,
quantity: state.quantity - 1,
};
case 'RESET_CART':
return initialState; // 初期状態に戻す
default:
throw new Error(`未知のアクションタイプです: ${action.type}`);
}
}
export default function ShoppingCart() {
// useReducerのセットアップ
const [state, dispatch] = useReducer(shoppingReducer, initialState);
// 合計金額の計算
const totalPrice = state.quantity state.pricePerItem;
return (
お買い物カート
商品名: {state.item}
単価: {state.pricePerItem}円
数量: {state.quantity}
合計金額: {totalPrice}円
);
}
このコードのポイントは、`reducer` 関数が「純粋関数(同じ入力に対して、常に全く同じ出力を返す関数)」になっているという点です。画面の描画とは切り離されて独立しているため、テストもしやすいし、バグが入り込む隙間がグッと減ります。
—
まとめ:いつ `useState` から `useReducer` に切り替えるべき?
「じゃあ、これからは全部 `useReducer` を使えばいいの?」と思われるかもしれませんが、答えは「NO」です。
- 単純なトグル(開閉の `true/false`)や、ひとつの入力フォームの文字入力など、シンプルで独立した状態なら `useState` が圧倒的にラクです。
- 一方で、
- ひとつの状態を更新するのに、あちこちの条件分岐が絡んで複雑になる
- 次の状態を作るために「前の状態の複数の値」をごちゃ混ぜに計算する必要がある
- チームメンバーや将来の自分がコードを見返したときに、「なんでここでこの値を書き換えてるんだっけ?」と迷いそうになる
そんな時は、迷わず `useReducer` と `dispatch` のコンビを召喚してください。最初は少しボイラープレート(お決まりの記述)が多くて面倒に感じるかもしれませんが、慣れてくると「あ、こっちの方が圧倒的にコードがきれいでお行儀がいいな」と実感できる瞬間が必ずやってきます。
焦らず、一歩ずつ、あなたのペースでReactと仲良くなっていきましょうね。応援しています!

コメント