useState - 状態管理の基礎
Reactで「変化する値」を扱うには、useStateを使います。
状態(State)とは?
状態とは、「時間とともに変化する値」のことです。
例:
- カウンター(クリックするたびに増える数値)
- 入力フォームの値
- モーダルの開閉状態
- チェックボックスのON/OFF
なぜuseStateが必要?
通常の変数では、Reactは再レンダリングしてくれません。
❌ 通常の変数(動かない)
function Counter() { let count = 0; // 通常の変数 const increment = () => { count = count + 1; console.log(count); // コンソールには表示されるが... }; return ( <div> <p>カウント: {count}</p> {/* 画面は更新されない! */} <button onClick={increment}>増やす</button> </div> ); }
✅ useState(動く)
import { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); // useState を使う const increment = () => { setCount(count + 1); // setCount で更新 }; return ( <div> <p>カウント: {count}</p> {/* 画面が更新される! */} <button onClick={increment}>増やす</button> </div> ); }
useStateの基本
書き方
const [状態変数, 更新関数] = useState(初期値);
- 状態変数: 現在の値を格納
- 更新関数: 値を更新するための関数
- 初期値: 最初の値
具体例
import { useState } from 'react'; function Example() { // countという状態を作る、初期値は0 const [count, setCount] = useState(0); return ( <div> <p>現在の値: {count}</p> <button onClick={() => setCount(count + 1)}> 増やす </button> </div> ); }
いろいろな型の状態
文字列の状態
function NameInput() { const [name, setName] = useState(""); return ( <div> <input type="text" value={name} onChange={(e) => setName(e.target.value)} /> <p>入力された名前: {name}</p> </div> ); }
真偽値の状態
function ToggleButton() { const [isOn, setIsOn] = useState(false); return ( <div> <p>状態: {isOn ? "ON" : "OFF"}</p> <button onClick={() => setIsOn(!isOn)}> 切り替え </button> </div> ); }
オブジェクトの状態
interface User { name: string; age: number; } function UserForm() { const [user, setUser] = useState<User>({ name: "", age: 0 }); return ( <div> <input type="text" placeholder="名前" value={user.name} onChange={(e) => setUser({ ...user, name: e.target.value })} /> <input type="number" placeholder="年齢" value={user.age} onChange={(e) => setUser({ ...user, age: Number(e.target.value) })} /> <p>名前: {user.name}, 年齢: {user.age}</p> </div> ); }
重要: オブジェクトを更新する時は、スプレッド構文(...user)を使って、他のプロパティを保持します。
配列の状態
function TodoList() { const [todos, setTodos] = useState<string[]>([]); const [input, setInput] = useState(""); const addTodo = () => { setTodos([...todos, input]); // 新しい配列を作る setInput(""); // 入力欄をクリア }; return ( <div> <input value={input} onChange={(e) => setInput(e.target.value)} /> <button onClick={addTodo}>追加</button> <ul> {todos.map((todo, index) => ( <li key={index}>{todo}</li> ))} </ul> </div> ); }
複数のuseStateを使う
1つのコンポーネントで、複数の状態を管理できます。
function LoginForm() { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [rememberMe, setRememberMe] = useState(false); const handleSubmit = () => { console.log({ email, password, rememberMe }); }; return ( <div> <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="メールアドレス" /> <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="パスワード" /> <label> <input type="checkbox" checked={rememberMe} onChange={(e) => setRememberMe(e.target.checked)} /> ログイン状態を保持 </label> <button onClick={handleSubmit}>ログイン</button> </div> ); }
よくある間違い
❌ 直接状態を変更しない
const [user, setUser] = useState({ name: "太郎", age: 25 }); // ダメな例 user.name = "花子"; // 直接変更してはダメ! // 正しい例 setUser({ ...user, name: "花子" });
❌ 更新関数を呼ばずに値を代入しない
const [count, setCount] = useState(0); // ダメな例 count = 5; // 直接代入してはダメ! // 正しい例 setCount(5);
前の状態に基づいて更新する
連続して状態を更新する場合は、関数形式を使います。
問題のある例
const [count, setCount] = useState(0); const incrementThreeTimes = () => { setCount(count + 1); setCount(count + 1); setCount(count + 1); // 期待: 3増える // 実際: 1しか増えない! };
正しい例
const [count, setCount] = useState(0); const incrementThreeTimes = () => { setCount(prev => prev + 1); setCount(prev => prev + 1); setCount(prev => prev + 1); // 期待通り3増える! };
実践例:簡単なカウンターアプリ
import { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); return ( <div style={{ textAlign: 'center', padding: '20px' }}> <h1>カウンター</h1> <p style={{ fontSize: '48px', fontWeight: 'bold' }}> {count} </p> <div> <button onClick={() => setCount(count - 1)}>-1</button> <button onClick={() => setCount(0)}>リセット</button> <button onClick={() => setCount(count + 1)}>+1</button> </div> </div> ); } export default Counter;
まとめ
- useState: 状態(変化する値)を管理するHook
- 書き方:
const [状態, 更新関数] = useState(初期値) - 更新方法: 更新関数を使う(直接代入しない)
- 複数の状態: 複数のuseStateを使える
- オブジェクト/配列: スプレッド構文で更新
- 関数形式: 前の状態に基づいて更新する時に使う
次は、副作用を扱うuseEffectを学びましょう!