🎯 このレッスンで学ぶこと
実践的なアプリケーションでは、1つのコンポーネントで複数の状態を管理する必要があります。 このレッスンでは、複数の状態を効率的に管理する方法を学びます。
このレッスンでは、以下の内容を学びます:
- 個別のuseState: それぞれの状態を独立して管理する方法
- オブジェクトのuseState: 関連する状態をまとめて管理する方法
- 状態の更新パターン: オブジェクト状態を正しく更新する方法
- 使い分けの判断基準: いつ個別の状態を使い、いつオブジェクトを使うか
1. 個別のuseStateを使う方法
最もシンプルな方法は、それぞれの状態に対して個別にuseStateを使うことです。 この方法は、状態が互いに独立している場合に最適です。
✅ 個別のuseStateが適している場合
- 状態が互いに独立している
- 状態を個別に更新することが多い
- コードがシンプルで読みやすい
📝 例:ユーザー情報フォーム
import { useState } from "react";
const UserForm = () => {
// 個別のuseStateで管理
const [name, setName] = useState("");
const [age, setAge] = useState(0);
const [email, setEmail] = useState("");
return (
<form>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="名前"
/>
<input
type="number"
value={age}
onChange={(e) => setAge(Number(e.target.value))}
placeholder="年齢"
/>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="メールアドレス"
/>
</form>
);
};
✅ メリット:
- 各状態が独立していて、更新が明確
- 一つの状態を更新しても他の状態に影響しない
- コードが読みやすく、理解しやすい
📸 実世界での状態管理のイメージ
✅ 個別のuseState = 個別の引き出し
📦 オブジェクトのuseState = 整理箱
💡 理解のポイント: 個別のuseStateは「それぞれ専用の引き出し」で、オブジェクトのuseStateは「1つの整理箱」のようなものです。 引き出しは1つを開けても他に影響しませんが、整理箱の中身を変える時は新しい箱を作る必要があります。
🔄 状態更新のフロー比較
✅ 個別のuseState
ユーザーが入力
name フィールドに "太郎" と入力
setName実行
setName("太郎")
nameだけ更新
age, emailは影響なし
再レンダリング
nameだけが変更される
📦 オブジェクトのuseState
ユーザーが入力
name フィールドに "太郎" と入力
新しいオブジェクト作成
...user で全てコピー
nameだけ上書き
age, emailはそのまま
setUser実行
新しいuserオブジェクトで更新
⌨️ フォーム入力時の状態変化の様子
初期状態
name
""
age
0
""
ユーザーがまだ何も入力していない状態
nameフィールドに "太郎" を入力
name
"太郎" ✨
更新された
age
0
変更なし
""
変更なし
setName("太郎") が実行され、nameだけが更新される
ageフィールドに 25 を入力
name
"太郎"
保持される
age
25 ✨
更新された
""
変更なし
setAge(25) が実行され、ageだけが更新される(nameはそのまま保持)
💡 ポイント: 個別のuseStateでは、1つの状態を更新しても他の状態には影響しません。 フォームの入力フィールドが独立して動作する様子が分かります。
📚 身近な例えで理解しよう:引き出しの例
個別のuseStateは、「名前用の引き出し」「年齢用の引き出し」「メール用の引き出し」を それぞれ用意するようなものです。それぞれの引き出しに物を入れたり出したりするのが 独立していて、他の引き出しに影響しません。
2. オブジェクトのuseStateを使う方法
関連する状態を1つのオブジェクトにまとめて管理する方法もあります。 この方法は、状態が関連している場合や、まとめて扱うことが多い場合に便利です。
📦 複数の状態を管理する方法の比較
方法1: 個別のuseState(推奨)
const [name, setName] = useState("");
const [age, setAge] = useState(0);
const [email, setEmail] = useState("");name
独立した状態
age
独立した状態
独立した状態
✅ メリット:
- 各状態が独立していて、更新が明確
- 一つの状態を更新しても他の状態に影響しない
- コードが読みやすい
方法2: オブジェクトのuseState
const [user, setUser] = useState({
name: "",
age: 0,
email: ""
});user オブジェクト
name
age
📝 使用例:
- 関連するデータをまとめて管理したい時
- フォームの入力値をまとめて扱いたい時
- 更新時にスプレッド構文(...)を使う必要がある
🌳 状態の構造を視覚化
✅ 個別のuseStateの構造
📦 オブジェクトのuseStateの構造
💡 構造の違い: 個別のuseStateは「フラットな構造」で、各状態が同じ階層に並んでいます。 一方、オブジェクトのuseStateは「階層的な構造」で、userという親オブジェクトの中に各プロパティが入っています。
📦 オブジェクトのuseStateが適している場合
- 状態が互いに関連している
- 状態をまとめて初期化・リセットすることが多い
- 状態を一つの単位として扱いたい
📝 例:ユーザー情報をオブジェクトで管理
import { useState } from "react";
const UserForm = () => {
// オブジェクトのuseStateで管理
const [user, setUser] = useState({
name: "",
age: 0,
email: ""
});
return (
<form>
<input
type="text"
value={user.name}
onChange={(e) =>
setUser({ ...user, name: e.target.value })
}
placeholder="名前"
/>
<input
type="number"
value={user.age}
onChange={(e) =>
setUser({ ...user, age: Number(e.target.value) })
}
placeholder="年齢"
/>
<input
type="email"
value={user.email}
onChange={(e) =>
setUser({ ...user, email: e.target.value })
}
placeholder="メールアドレス"
/>
</form>
);
};
✅ メリット:
- 関連するデータを一つの単位として扱える
- 状態をまとめてリセットしたり、初期化しやすい
- APIから取得したデータ構造と一致させやすい
3. オブジェクト状態の更新方法
オブジェクトの状態を更新する時は、新しいオブジェクトを作成する必要があります。 Reactは、同じオブジェクト参照を検知できないため、直接変更しても再レンダリングされません。
🔄 オブジェクト状態の更新パターン
間違った更新方法
// ❌ これは動きません!
user.name = "新しい名前";
setUser(user);⚠️ 問題点:
- Reactは同じオブジェクト参照を検知できない
- 状態が更新されても再レンダリングされない
- オブジェクトを直接変更(ミューテーション)してはいけない
方法1: スプレッド構文を使う(推奨)
// ✅ 新しいオブジェクトを作成
setUser({ ...user, name: "新しい名前" });古いuser
name: "旧"
age: 20
新しいuser
name: "新"
age: 20
...userで全ての値をコピー → nameだけを上書き
方法2: 関数型更新を使う
// ✅ 前の状態を使って更新
setUser(prev => ({ ...prev, name: "新しい名前" }));💡 複数の更新を連続で行う場合に便利です
📦 身近な例えで理解しよう:引っ越しの例
❌ 間違った方法: 引っ越し先の住所を直接書き換えるのではなく、新しい住所が書かれた新しい書類を作る必要があります。 同じ書類を書き換えてしまうと、システムが「変更された」と認識できません。
✅ 正しい方法: 古い住所が書かれた書類をコピーして(...user)、住所だけを新しいものに書き換えます。 これで新しい書類として認識され、状態が正しく更新されます。
⚠️ 重要なポイント:イミュータビリティ(不変性)
Reactでは、状態を直接変更(ミューテーション)してはいけません。 必ず新しいオブジェクトや配列を作成して、状態を更新する必要があります。 これがReactの「イミュータビリティ(不変性)」という原則です。
📝 複数のプロパティを一度に更新する例
const handleSubmit = () => {
// スプレッド構文で全ての値をコピーして、必要な部分だけ更新
setUser({
...user,
name: "新しい名前",
age: 25
});
// または関数型更新を使う
setUser((prev) => ({
...prev,
name: "新しい名前",
age: 25
}));
};
4. 使い分けの判断基準
個別のuseStateとオブジェクトのuseState、どちらを使うべきか判断する基準を学びましょう。
✅ 個別のuseStateを使う場合
- •状態が互いに独立している
- •状態を個別に更新することが多い
- •シンプルなコンポーネント
- •状態の数が少ない(2〜3個程度)
📦 オブジェクトのuseStateを使う場合
- •状態が互いに関連している
- •状態をまとめて扱うことが多い
- •APIのデータ構造と一致させたい
- •状態を一括でリセットしたい
💡 実践的なアドバイス
最初は個別のuseStateから始めることをおすすめします。 コードがシンプルで理解しやすく、ほとんどの場合で十分です。 状態が増えてきたり、関連性が強くなってきたら、オブジェクトのuseStateに移行することを検討しましょう。
📚 まとめ
🎯 覚えておきたいポイント
- 個別のuseStateは、独立した状態を管理するのに最適です
- オブジェクトのuseStateは、関連する状態をまとめて管理するのに便利です
- オブジェクト状態を更新する時は、必ず新しいオブジェクトを作成します(スプレッド構文を使う)
- 状態を直接変更(ミューテーション)してはいけません
- 最初は個別のuseStateから始めて、必要に応じてオブジェクトに移行しましょう