こんにちは!Reactの世界へようこそ。チーフアーキテクトの私です。
Reactを触り始めて最初に覚える魔法の呪文、それが `useState` ですよね。「画面のデータを覚えておいて、変わったら再描画してくれる」という超便利機能ですが、しばらくコードを書いていると、こんな疑問にぶつかりませんか?
「これ、一つのオブジェクトにまとめた方がいいの?それとも、バラバラに `useState` を分けた方がいいの?」
ネットで調べても「オブジェクトの方がスッキリするよ」「いや、細かい方がバグりにくいよ」など、人によって言うことが違ってパニックになりがちです。大丈夫です、安心してください。今日のこの記事を読めば、そのモヤモヤがスッキリ氷解しますよ。
今回は、身近な「お買い物フォーム」を例に、現場のプロがどう考えて状態を設計しているのかを、優しく紐解いていきましょう!
—
1. そもそも `useState` ってどんなイメージ?
まずは基本のおさらいです。`useState` は、いわば「自分専用の小さなロッカー」だと思ってください。
ロッカーには名前(状態の名前)がついていて、中身(データ)を自由に入れ替えることができます。
- 個別の `useState`(ロッカーをたくさん並べるスタイル):
「お財布専用のロッカー」「スマホ専用のロッカー」「鍵専用のロッカー」と、持ち物ごとに小さな引き出しをたくさん用意するイメージです。
- オブジェクトの `useState`(大きなタンスを置くスタイル):
「持ち物タンス」という大きな家具をドンと置き、その中の「引き出しAはお財布、引き出しBはスマホ…」と管理するイメージです。
どちらが良い・悪いというよりも、「中身の性質」や「開け閉めする頻度」によって使い分けるのがコツなんです。
—
2. バラバラ派?オブジェクト派?お買い物フォームで比較しよう
では、よくある「名前」と「メールアドレス」を入力するフォームを例に、それぞれの書き方を見てみましょう。
パターンA:個別の `useState` に分ける(ロッカーたくさんスタイル)
import { useState } from ‘react’;
function SignupFormSeparate() {
// 名前専用のロッカー
const [name, setName] = useState(”);
// メールアドレス専用のロッカー
const [email, setEmail] = useState(”);
return (
【パターンA】バラバラに分けた場合
setName(e.target.value)}
/>
setEmail(e.target.value)}
/>
);
}
ここがイイね!:
お名前を変えたときは `setName` を呼ぶだけ。他のデータ(メールアドレス)のことは一切気にする必要がありません。思考がシンプルで、バグが入り込みにくいのが最大のメリットです。初学者のうちは、迷ったらまずこっちをおすすめします!
—
パターンB:1つのオブジェクトにまとめる(大きなタンススタイル)
import { useState } from ‘react’;
function SignupFormObject() {
// フォーム全体のデータを「1つの大きなタンス(オブジェクト)」として管理する
const [formData, setFormData] = useState({
name: ”,
email: ”,
});
const handleChange = (e) => {
const { name, value } = e.target;
// 【超重要】オブジェクトを更新するときの作法(スプレッド構文)
// 「今までのタンスの中身を全部コピーして (…) 、変えたい場所だけ上書きする」
setFormData((prevData) => ({
…prevData,
[name]: value,
}));
};
return (
【パターンB】オブジェクトにまとめた場合
);
}
ここがイイね!:
入力項目が10個、20個と増えてきたとき、`useState` が20個並ぶとコードがグチャグチャになりますよね。そんなとき、この「タンススタイル」にまとめると、サーバーにデータを「はい、ドン!」と送信するときに非常にスッキリします。
ただ、見慣れない `…prevData`(スプレッド構文)が出てきて、「うっ…」となったかもしれません。大丈夫、これは「これまでの思い出(既存のデータ)を捨蔽せずに、新しい思い出を上書きする魔法の儀式」だと覚えておいてください。
—
3. 現場のプロが教える!設計の「黄金ルール」
「じゃあ結局、どっちを使えばいいの?」という疑問に、現場のチーフとしてズバッとお答えしましょう。
ルールその1:お互いに全く関係のないデータなら、絶対に分ける!
例えば、「ユーザーの名前(文字列)」と「モーダルが開いているかどうか(真偽値:true/false)」は、お互いに何の関係もありませんよね。
こういうものを一つのオブジェクトにまとめると、「名前を変えたついでに、うっかりモーダルの状態まで消しちゃった…」という悲しいバグ(データの上書きミス)の元になります。関係のないものは、迷わずロッカーを分けましょう。
ルールその2:同時に更新され、セットで扱いたいならオブジェクトにする!
例えば、画面上の「マウスのX座標とY座標( `{ x: 100, y: 200 }` )」のように、「いつも一緒にペアで動くよね」というデータは、一つのオブジェクトにまとめた方が、コードを読む人が「あ、これはセットなんだな」と直感的に理解しやすくなります。
ルールその3:入力フォームは「数」で決めるべし!
- 項目が2〜3個のシンプルなログイン画面 = 個別の `useState` でOK!
- アンケートや会員登録などで項目が10個以上ある = オブジェクトでまとめるとスッキリ!
—
4. やりがちな「非同期バグ」に気をつけて!
最後に、オブジェクトや複数ステートを扱うときに初学者が一番ハマりやすい「罠」についてお話しします。
Reactのステート更新は、「お願いしてから、実際に部屋の模様替えが終わるまでに少しタイムラグがある(非同期)」という特徴があります。
もし、前回の古いデータをそのまま信じて更新しようとすると、こんな恐ろしいことが起きます。
// ❌ やりがちなミス(古い状態をそのまま使ってしまう)
setFormData({ name: ‘新しい名前’ });
// あれ? email のデータが消えちゃった!?(タンスごと買い替えてしまった状態)
正解は、先ほどのコードでも書いた通り、「直前の状態(`prev` とよく呼ばれます)を必ず受け取って、そこに継ぎ足す」書き方をすることです。
// ⭕ 正解の書き方
setFormData((prev) => ({
…prev, // 古い中身をここにバーッと展開して
name: ‘新しい名前’ // 書き換えたいところだけ上書き!
}));
この「`prev => ({ …prev, … })`」というお作法は、オブジェクトの状態管理をする上での合言葉です。ぜひお守り代わりに覚えておいてくださいね。
—
まとめ
いかがでしたでしょうか?
- `useState` は「小さなロッカー(個別)」と「大きなタンス(オブジェクト)」の使い分けが肝心。
- 関係ないデータは混ぜるな危険!迷ったらロッカーを分けよう。
- オブジェクトを更新するときは、スプレッド構文(`…prev`)で優しく上書きしてあげよう。
Reactのコードを書くときは、画面の向こうにいるユーザーだけでなく、「未来の自分」や「一緒に働く仲間」が読みやすい優しさを持つことが、実は一番のアーキテクチャの極意だったりします。
あなたのReactライフが、少しでも楽しく、スムーズになりますように。
それでは、また次の現場でお会いしましょう!

コメント