🎯 このレッスンで学ぶこと
状態の更新には様々なパターンがあります。適切な更新方法を選ぶことで、 コードが読みやすくなり、バグを防ぐことができます。
このレッスンでは、以下の内容を学びます:
- 直接値での更新: シンプルな値の更新方法
- 関数型更新: 前の値を使った安全な更新方法
- オブジェクトの更新: スプレッド構文を使った更新
- 配列の更新: 追加、削除、更新のパターン
- 条件付き更新: 特定の条件での更新
1. 基本的な状態更新
最もシンプルな更新方法は、新しい値を直接渡すことです。 文字列や数値などの単純な値の場合、この方法が最も適しています。
📝 基本的な更新の例
import { useState } from "react";
const Counter = () => {
const [count, setCount] = useState(0);
const [name, setName] = useState("");
const [isActive, setIsActive] = useState(false);
return (
<div>
<button onClick={() => setCount(5)}>
カウントを5に設定
</button>
<input
value={name}
onChange={(e) => setName(e.target.value)}
/>
<button onClick={() => setIsActive(true)}>
アクティブにする
</button>
</div>
);
};
💡 新しい値を直接渡すだけで、状態を更新できます。
2. 関数型更新(前の値を使う)
前の値を使って計算したい場合は、関数型更新を使います。 これにより、連続して状態を更新しても正しく動作します。
🔄 直接値 vs 関数型更新の比較
📝 直接値で更新
setCount(count + 1);現在の値: count = 5
計算: 5 + 1 = 6
新しい値: count = 6
⚠️ 連続して呼び出すと問題が発生する可能性
⚡ 関数型更新(推奨)
setCount(prev => prev + 1);前の値を受け取る
計算: prev + 1
新しい値: count = 6
✅ 連続して呼び出しても安全
💡 違い: 直接値で更新する場合、複数回連続して呼び出すと古い値を使って計算してしまいます。 関数型更新では、常に最新の値(prev)を使って計算するため、連続呼び出しでも正しく動作します。
📝 関数型更新の例
import { useState } from "react";
const Counter = () => {
const [count, setCount] = useState(0);
// ❌ 問題がある例
const incrementTwiceWrong = () => {
setCount(count + 1); // 0 + 1 = 1
setCount(count + 1); // 0 + 1 = 1 (同じ値)
// 結果: count = 1 (期待: 2)
};
// ✅ 正しい例(関数型更新)
const incrementTwiceCorrect = () => {
setCount(prev => prev + 1); // 0 + 1 = 1
setCount(prev => prev + 1); // 1 + 1 = 2
// 結果: count = 2 ✓
};
return (
<div>
<p>カウント: {count}</p>
<button onClick={incrementTwiceCorrect}>
+2(関数型更新)
</button>
</div>
);
};
✅ 関数型更新のメリット:
- 連続して状態を更新しても正しく動作する
- 最新の値を使って計算できる
- 非同期処理でも安全
📚 身近な例えで理解しよう:銀行の残高の例
直接値: 「現在の残高は1000円です。+500円してください」と伝えると、 2回伝えても「1000 + 500 = 1500円」のままです。
関数型更新: 「現在の残高に+500円してください」と伝えると、 2回伝えると「1000 → 1500 → 2000円」と正しく増えます。
3. オブジェクトの状態更新
オブジェクトの状態を更新する時は、スプレッド構文(...)を使って、 新しいオブジェクトを作成する必要があります。
🔄 オブジェクト状態の更新パターン
間違った更新方法
// ❌ これは動きません!
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)、住所だけを新しいものに書き換えます。 これで新しい書類として認識され、状態が正しく更新されます。
📝 オブジェクト状態の更新例
import { useState } from "react";
const UserProfile = () => {
const [user, setUser] = useState({
name: "太郎",
age: 25,
email: "taro@example.com"
});
// ✅ 正しい方法:スプレッド構文を使う
const updateName = (newName) => {
setUser({ ...user, name: newName });
};
// ✅ 複数のプロパティを更新
const updateProfile = (newName, newAge) => {
setUser({ ...user, name: newName, age: newAge });
};
// ✅ 関数型更新を使う方法
const updateAge = () => {
setUser(prev => ({ ...prev, age: prev.age + 1 }));
};
return (
<div>
<p>名前: {user.name}</p>
<p>年齢: {user.age}</p>
<button onClick={() => updateAge()}>
年齢を+1
</button>
</div>
);
};
4. 配列の状態更新
配列の状態を更新する時も、新しい配列を作成する必要があります。 追加、削除、更新の基本的なパターンを学びましょう。
📋 配列の状態更新パターン
➕ 要素を追加する
setItems([...items, newItem]);➖ 要素を削除する
setItems(items.filter(item => item.id !== id));✏️ 要素を更新する
setItems(items.map(item =>
item.id === id ? {...item, ...updates} : item
));📝 配列操作の例
import { useState } from "react";
const TodoList = () => {
const [items, setItems] = useState(["タスク1", "タスク2"]);
// ✅ 要素を追加
const addItem = (newItem) => {
setItems([...items, newItem]);
// または
setItems(items.concat(newItem));
};
// ✅ 要素を削除
const removeItem = (index) => {
setItems(items.filter((_, i) => i !== index));
};
// ✅ 要素を更新
const updateItem = (index, newValue) => {
setItems(items.map((item, i) =>
i === index ? newValue : item
));
};
// ✅ 先頭に追加
const addToStart = (newItem) => {
setItems([newItem, ...items]);
};
return (
<div>
{items.map((item, index) => (
<div key={index}>{item}</div>
))}
</div>
);
};
✅ 配列操作のベストプラクティス:
- 追加:
[...items, newItem]またはitems.concat(newItem) - 削除:
items.filter(...) - 更新:
items.map(...)
5. 条件付き更新
特定の条件が満たされた時だけ状態を更新したい場合もあります。 このような場合、条件分岐を使って更新を制御します。
📝 条件付き更新の例
import { useState } from "react";
const Counter = () => {
const [count, setCount] = useState(0);
// ✅ 最大値の制限
const incrementWithMax = () => {
setCount(prev => {
if (prev >= 10) {
return prev; // 10以上なら更新しない
}
return prev + 1;
});
};
// ✅ 最小値の制限
const decrementWithMin = () => {
setCount(prev => prev > 0 ? prev - 1 : 0);
};
// ✅ 条件に応じた更新
const updateConditionally = (newValue) => {
if (newValue > 0 && newValue < 100) {
setCount(newValue);
}
};
return (
<div>
<p>カウント: {count}</p>
<button onClick={incrementWithMax}>+1(最大10まで)</button>
<button onClick={decrementWithMin}>-1(最小0まで)</button>
</div>
);
};
📚 まとめ
🎯 覚えておきたいポイント
- 直接値: シンプルな値(文字列、数値、真偽値)の更新に適している
- 関数型更新: 前の値を使う場合は必ず関数型更新を使う(連続更新でも安全)
- オブジェクト: スプレッド構文(
...object)を使って新しいオブジェクトを作成する - 配列:
filter、map、concatなどを使って新しい配列を作成する - 条件付き更新: 条件分岐を使って、必要な時だけ状態を更新する