React: Stateの基本:useState
最終更新:2026-10-26
ステートとは、コンポーネントのメモリのことです。プロップが他者から渡されるパラメータ(読み取り専用)であるのに対し、ステートは自分専用の小さなノート(編集可能)のようなものです。useStateは、Reactがコンポーネントに組み込む「メモリチップ」のようなものです。
1. 学習内容
- 「ステート」とは何ですか?また、なぜステートが必要なのでしょうか?
- useState の基本的な使い方
- 配列とオブジェクトの状態を更新する正しい方法(不変性)
- 複数の状態変数の管理
- 状態のリフトアップ
2. カウンターが引き起こした考察
(1) 課題:通常の変数ではUIの更新がトリガーされない
ボブは簡単なカウンターを作りたいと思っています。ボタンをクリックすると、数字が1ずつ増えるようなものです。
▶ 試してみようボブはある問題に気づきました。通常の変数では、ReactにUIを更新するよう通知されないのです。コンポーネントが再レンダリングされるたびに、countは0にリセットされてしまいます。
(2) useState を用いた解決策
▶ 試してみよう
戻り値:useState は配列 [current value, setter function] を返します。setCount を呼び出した後、React は自動的にコンポーネントを再レンダリングし、最新の値を表示します。
3. useState の基本概念
(1) 分割代入
import { useState } from 'react'
// useState(initialValue) Back [value, setValue]
const [count, setCount] = useState(0)
// ^ ^ ^
// | | └─ Initial value(Applies only to the first render)
// | └─ Update Function(Triggers a re-render after being called)
// └─ Current status value(Get the latest value on every render)
| セクション | 説明 |
|---|---|
count |
現在の状態値。レンダリングのたびに最新の値が取得される |
setCount |
update関数:これを呼び出すと → Reactがコンポーネントを再レンダリングする |
useState(0) |
初期値(最初のレンダリング時に使用され、それ以降のレンダリングでは無視される) |
(2) 更新メカニズム
▶ 試してみよう重要な概念:
setCount(count + 1)は、「countを即座に変更する」のではなく、「次回のレンダリング時にcountを更新するよう React に指示する」という意味です。これは 非同期更新 と呼ばれます。
(3) 関数型更新
新しい状態が古い状態に依存する場合は、関数型更新を使用する必要があります:
▶ 試してみよう| メソッド | 構文 | 特徴 |
|---|---|---|
| 直接更新 | setCount(count + 1) |
シンプルだが、連続して呼び出すとデータが失われる |
| 機能アップデート | setCount(prev => prev + 1) |
正確;連続した呼び出しでも正常に動作する |
4. 配列とオブジェクトの更新(不変性)
React では、状態を直接変更してはなりません。state.push() や state.name = 'xxx' は絶対に使用しないでください。代わりに、新しい配列またはオブジェクトを作成して、古い値と置き換えてください。
(1) 更新対象のオブジェクト
▶ 試してみよう(2) 配列を更新する
▶ 試してみよう出力:
状態:arr
▶ サンプル:配列演算のクイックリファレンス表
// ============================================
// Example:React Array Operations in C 6 Common Scenarios
// ============================================
const [arr, setArr] = useState([1, 2, 3])
// 1. Append to the end
setArr([...arr, 4]) // [1, 2, 3, 4]
// 2. Add to the beginning
setArr([0, ...arr]) // [0, 1, 2, 3]
// 3. Insert in the middle
const insertAt = 1
setArr([...arr.slice(0, insertAt), 99, ...arr.slice(insertAt)])
// [1, 99, 2, 3]
// 4. Delete Element(filter)
setArr(arr.filter(n => n !== 2)) // [1, 3]
// 5. Update Element(map)
setArr(arr.map(n => n === 2 ? 22 : n)) // [1, 22, 3]
// 6. Sort(Copy first, then sort)
setArr([...arr].sort((a, b) => b - a)) // [3, 2, 1]
出力:
配列の状態操作:push → 項目の追加、filter → 項目の削除、map → 項目の更新。各操作は新しい配列参照を返すため React が再レンダリングされる
5. 複数の状態変数
コンポーネントには複数の状態変数を設定できます。それらを1つの巨大なオブジェクトに詰め込むのではなく、ロジックごとに分割することをお勧めします:
function RegistrationForm() {
// ✅ Recommendations:Logical Breakdown
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [isSubmitting, setIsSubmitting] = useState(false)
const [errors, setErrors] = useState({})
// ❌ Not recommended:All states are stored in a single object
const [form, setForm] = useState({
name: '', email: '', password: '',
isSubmitting: false, errors: {}
})
// Expand the entire object when updating,Fields that are easy to overlook
}
| 戦略 | メリット | デメリット |
|---|---|---|
複数の useState に分割する |
更新が正確で、型安全、かつ読みやすい | 状態が多いと変数が多くなりすぎる |
1つのuseStateにまとめられている |
複数のフィールドを一度に更新するのに便利 | 更新の構文が煩雑で、展開し忘れることがよくある |
推奨事項:独立した状態(入力フィールド、読み込みインジケーター、エラーメッセージなど)には複数の
useStateインスタンスを使用してください。論理的に関連するフィールド(ユーザープロファイル内のすべてのフィールドなど)は、1つのオブジェクトにまとめることができます。
6. 状態の引き上げ
複数のコンポーネントが同じ状態を共有する必要がある場合は、その状態を、それらに最も近い共通の親コンポーネントへと引き上げてください。
graph TB
subgraph "Before the status upgrade(Data is not shared)"
A[Parent Component App] --- B[Child component A<br/>Have their own count]
A --- C[Child component B<br/>Have their own count]
end
subgraph "After the status upgrade(Data Sharing)"
D[Parent Component App<br/>**count Here**] --- E[Child component A<br/>Read count, Call setCount]
D --- F[Child component B<br/>Read count, Call setCount]
end
出力:
状態:arr(setter: setArr)
▶ サンプル:状態のリフトアップの実践的な活用
▶ 試してみよう出力:
摂氏で 100 を入力 → 華氏で自動的に 212.0 が表示される。表示 "100°C = 212.0°F"。2 つの入力ボックスが同期している。
7. 完全な例:ショッピングカートのカウンター
▶ 実行例を展開(96 行)
出力:
サブタイトル:"Temperature Converter"。表示:"Celsius(°C):"。状態:temperature(setter: setTemperature)
インタラクションの流れ:
- 「+/−」をタップして数量を調整すると、小計と合計がリアルタイムで更新されます
- ✕をクリックして商品を削除する。ショッピングカートが空のときは、わかりやすいメッセージを表示する
- プロモーションコード
REACT2026を入力すると、20元割引になります
▶ サンプル:オブジェクトの状態の更新—ユーザー情報の編集
function UserEditor() {
const [user, setUser] = useState({ name: 'Alice', email: 'alice@test.com', age: 28 })
function updateField(field, value) {
setUser(prev => ({ ...prev, [field]: value }))
}
return (
<div style={{ maxWidth: 400, margin: '0 auto' }}>
<h3>Edit Profile</h3>
<input value={user.name} onChange={e => updateField('name', e.target.value)}
placeholder="Name" style={{ width: '100%', padding: 8, marginBottom: 8, borderRadius: 4 }} />
<input value={user.email} onChange={e => updateField('email', e.target.value)}
placeholder="Email" style={{ width: '100%', padding: 8, marginBottom: 8, borderRadius: 4 }} />
<input type="number" value={user.age} onChange={e => updateField('age', Number(e.target.value))}
placeholder="Age" style={{ width: '100%', padding: 8, marginBottom: 8, borderRadius: 4 }} />
<pre style={{ background: '#f5f5f5', padding: 12, borderRadius: 4, fontSize: 13 }}>
{JSON.stringify(user, null, 2)}
</pre>
</div>
)
}
出力:
状態:address。入力フィールド:Street、City、ZIP
出力:
表示:"Edit Profile"。状態:user(setter: setUser)。入力:Name、Email、Age
▶ サンプル:ネストされたオブジェクトの状態 — 住所管理
▶ 試してみよう出力:
配送先フォーム:Street、City、ZIP、国ドロップダウン(US/CN/JP)、"Primary address" チェックボックス。すべてのフィールドがリアクティブに状態を更新する。
出力:
表示:"Shipping Address"。状態:address(setter: setAddress)。入力:Street、City、ZIP。ドロップダウン:United States、China、Japan
▶ サンプル:関数による更新でバッチ更新の問題を解決する
▶ 試してみよう出力:
Score: 0、Multiplier: x1。+10/+50/+100 は スコア×倍率 で加点。"2x Multiplier" は倍率を倍にする(最大 x8)。Reset → Score: 0、x1
❓ よくある質問
useStateと通常の変数では、パフォーマンスにどのような違いがありますか?useStateは追加のオーバーヘッド(変更の追跡、レンダリングのスケジューリング)を伴うため、「変更が発生した際にUIの更新がトリガーされる」ようなシナリオでのみ使用すべきです。変数が変更されても UI の更新を必要としない場合(タイマー ID やスクロール位置など)、useRef(第 10 課)を使用する方が適切です。useStateをいくつまで含めることができますか?パフォーマンス上の問題はありますか?useStateに分割することを推奨しています。各useStateの呼び出しは「状態単位」であり、Reactは多数のuseStateを効率的に処理できます。一般的なコンポーネントには、通常3~8個のuseStateが含まれます。setState の更新は同期的ですか、それとも非同期的ですか?setState はイベントハンドラ内では「自動的にバッチ処理」されていましたが、setTimeout および Promise の呼び出し内では同期的に実行されていました。React 18では「自動バッチ処理」が導入されたため、setStateはイベントハンドラ、setTimeout、Promise、fetchのコールバック内で呼び出されるかどうかにかかわらず、非同期バッチ処理されるようになりました。更新されたDOMに同期的にアクセスする必要がある場合は、flushSync(() => setState(...))を使用できます。📖 まとめ
- ステートはコンポーネントの「メモリ」のようなもので、これが変化すると、自動的に UI の更新がトリガーされます
useState(initialValue)は[value, setValue]配列を返します- 不変性:状態を直接変更してはならない。代わりに、新しいオブジェクトや配列を作成して、それを置き換えること。
- 機能アップデート
setCount(prev => prev + 1)は、以前の値に依存する場合、より正確になります - 状態の昇格:複数の子コンポーネントでデータを共有する必要がある場合、その状態は共通の親コンポーネントに昇格されます。
📝 練習問題
- 基本演習(難易度 ⭐):クリックすると ❤️ と 🤍 の状態を切り替え、いいねの数を表示する
LikeButtonコンポーネントを作成してください。 - 上級演習(難易度 ⭐⭐):
useStateを使用して経費のリスト(説明、金額、日付)を管理し、項目の追加や削除に対応するExpenseTrackerコンポーネントを作成してください。 - 課題(難易度:⭐⭐⭐):3つの
useStateインスタンスを使用してR、G、Bのスライダー値(0~255)を管理し、リアルタイムの色プレビューと16進数のカラーコードを表示するColorPickerコンポーネントを作成してください。