こんにちは!フロントエンドの現場を渡り歩いてきたチーフアーキテクトの私です。
Reactを触り始めると、避けて通れないのが「状態管理(State Management)」ですよね。特に`useState`を使って画面のデータをゴニョゴニョいじっているうちに、「あれ?なんだか値が古いまま更新されちゃうぞ…?」とか「データが増えてきて、どこでどう書き換えてるのか自分でも分からなくなった!」なんて泥沼にハマった経験はありませんか?
大丈夫ですよ、安心してください。
それ、世界中のすべてのReactエンジニアが最初に通る「登竜門」ですから。誰もが最初は同じところで盛大につまずきます。
今日は、そのカオスになりがちな状態の更新を、まるで「お買い物の注文書」のようにピシッと綺麗に整理整頓するための魔法――「アクションオブジェクトの設計とペイロードの受け渡し」について、一緒に優しく紐解いていきましょう。
—
状態管理がカオスになる「本当の理由」
Reactの`useState`は、本当にシンプルで強力です。
例えば、「カウントを増やす」「名前を変える」といった単純なデータなら、直感的に扱えて最高ですよね。
// 単純な状態ならuseStateで十分すぎるほどシンプル
const [count, setCount] = useState(0);
しかし、これが「ショッピングカート」や「ユーザー登録フォーム」「複雑な設定画面」のように、管理するデータが複数絡み合ってくるとどうでしょう?
「ボタンを押した場所によって、更新するルールがバラバラ」「どこからでも`setCount`を呼べるせいで、いつの間にかデータが変な形に書き換わっている……」なんてことが起こり始めます。
ここで現場のプロたちがどうするかと言うと、「データの書き換え方を、あらかじめ『お墨付きの注文書(アクション)』として定義しておく」というアプローチを取ります。
—
身近な例え:ファミレスの注文票で考えてみる
想像してみてください。
あなたが人気ファミレスのホールスタッフだとします。お客さんから「ハンバーグランチ、ライス大盛りで!」と口頭でめちゃくちゃに注文を受けたら、キッチンにどう伝えますか?
きっとメモ用紙に、こう書きますよね。
- 注文の種類(何をするの?): ハンバーグランチを追加
- 中身の詳細(データは?): ライス大盛り、ソースはデミグラス
Reactの状態管理における「アクションオブジェクト」も、まったくこれと同じです。
- `type`: 何をしたいのか(注文の種類)
- `payload`: そのために必要なデータ(中身の詳細)
この2つのセットを作るだけで、データの更新作業が一気にクリアに見通し良くなるんです。
—
標準的なアクション設計:`type` と `payload` のコンビネーション
実際のコードを見てみましょう。
ここでは、みんな大好き「お買い物カート」を例に、アクションオブジェクトの形を組んでみます。
// カートの中身を操作するための「注文書(アクション)」の例
// 1. カートに商品を新しく追加するアクション
const addItemAction = {
type: ‘ADD_ITEM’, // 「何をするか」を表すラベル(基本は大文字で書くのが業界の暗黙の了解です)
payload: { id: 1, name: ‘極上おいしいコーヒー豆’, price: 1200 } // 「どんなデータを追加するか」の荷物(ペイロード)
};
// 2. カートから商品を削除するアクション
const removeItemAction = {
type: ‘REMOVE_ITEM’,
payload: { id: 1 } // 「どのIDの商品を消すか」という最小限のデータだけを乗せる
};
どうですか?すごくシンプルですよね。
「何をしたいのか(`type`)」と「その中身(`payload`)」がきれいに分かれているので、あとからコードを読んだときに「あ、ここでカートに商品を追加してるんだな」と一目でわかるようになります。
—
実践!`useReducer` と組み合わせて使ってみよう
Reactには、この「注文書(アクション)」の仕組みを標準で受け止めてくれる、`useReducer` というめちゃくちゃ頼れるフックが用意されています。
「`useState`の親戚みたいなものかな?」と思っておけばOKです。実際に動くコードを見てみましょう。
import React, { useReducer } from ‘react’;
// 1. 最初は空っぽのショッピングカートからスタート
const initialState = {
items: [],
totalPrice: 0,
};
// 2. 注文書(アクション)を受け取って、実際にデータをどう書き換えるか決める「厨房(レデューサー)」
function cartReducer(state, action) {
// actionの「type(注文の種類)」によって、処理を分岐します
switch (action.type) {
case ‘ADD_ITEM’:
// payloadに入っている新しい商品を、今のカートに追加!
return {
…state, // 今までの状態をそのままコピーして
items: […state.items, action.payload], // 商品リストの末尾に新しい商品を追加
totalPrice: state.totalPrice + action.payload.price, // 合計金額も一緒に計算!
};
case ‘REMOVE_ITEM’:
// payloadに入っているID以外の残りの商品をフィルターで残す
const filteredItems = state.items.filter(item => item.id !== action.payload.id);
const removedItem = state.items.find(item => item.id === action.payload.id);
return {
…state,
items: filteredItems,
totalPrice: state.totalPrice – (removedItem ? removedItem.price : 0),
};
default:
// 万が一、知らない注文書が来たら、そのままの状態を返す(バグ防止の安全装置)
return state;
}
}
export default function ShoppingCart() {
// useReducerに「厨房(reducer)」と「初期状態」をセットする
// state: 現在のカートの状態, dispatch: 注文書を厨房に送るための係員
const [state, dispatch] = React.useReducer(cartReducer, initialState);
// 商品を追加するボタンが押されたときの関数
const handleAddCoffee = () => {
// 注文書(アクションオブジェクト)を作って、dispatch(係員)に渡す!
dispatch({
type: ‘ADD_ITEM’,
payload: { id: 101, name: ‘深煎りブレンド’, price: 800 }
});
};
return (
お買い物カート
現在の合計金額: {state.totalPrice}円
カートの中身 ({state.items.length}点)
-
{state.items.map((item, index) => (
- {item.name} – {item.price}円
))}
);
}
—
現場で役立つ!ちょっとしたコツと「型安全」の話
ここまで読んだあなたは、「`payload`の中に何を入れてもいい自由がある分、書き間違えちゃいそうだな…」と感じたかもしれません。その直感、素晴らしいです!まさにその通りで、現場では「あれ、`payload`の中に`id`を入れたつもりが`itemId`になってた!」なんてケアレスミスがよく起きます。
もしTypeScriptを使える環境であれば、次のように「注文書の型」をあらかじめガッチリ決めておくのが、プロたちの定番スタイルです。
// TypeScriptの例:注文書の設計図を作る
type CartAction =
| { type: ‘ADD_ITEM’; payload: { id: number; name: string; price: number } }
| { type: ‘REMOVE_ITEM’; payload: { id: number } };
// こうしておくと、万が一「type」のスペルを間違えたり、
// payloadに必要なデータを入れ忘れたりすると、エディタが赤く波線を引いて「ここ間違ってるよ!」と優しく教えてくれます。
最初は少しルールが多くて難しく感じるかもしれませんが、アプリの規模が大きくなればなるほど、この「型にはまったアクション設計」があなたを助けてくれます。コードのバグが激減し、自分の書いたコードに自信が持てるようになるはずです。
—
最後に
Reactの状態管理は、最初は本当に複雑怪奇に見えます。
「なんでこんなに書き方ルールがあるんだよ!」とイライラすることもあるでしょう。でも、今回紹介した`type`(種類)と`payload`(荷物)の組み合わせという「共通言語」を持つことで、コードの迷子が驚くほど減っていきます。
焦らず、一歩ずつ、あなたのペースでコードを書いていってくださいね。
今日書いたその1行が、未来のあなたを確実に助けてくれますよ。それでは、快適なReactライフを!

コメント