C
React/状態管理/Lesson 07

useState

30分·theory
このチャプター
1/2
JavaScript

useState

💡 なぜ学ぶべきなのか?

🎯 Reactで画面に表示される値を保存・更新するための最も基本的な方法です。
💼 ボタンのクリックやテキスト入力などのユーザー操作に応答するために必須です。
すべてのReact開発者が毎日使う、最も重要な機能です。
🏢 실무에서는
実務では「いいね」ボタンを押すと数字が増加したり、トグルボタンでメニューを開閉したりする際にuseStateを使用します。ECサイトの商品数量を増減するボタンもすべてuseStateで実装されます。

useState — コンポーネントの記憶:値が変われば画面も変わる

身近なたとえ:黒板

コンポーネントは黒板を持っています。

  • state: 黒板に書かれた値
  • setState: 黒板を消して新しい値を書く行為
  • 黒板が変わると → 画面が自動的に再描画されます

なぜ学ぶのか?

通常の変数を変更しても画面は更新されません。

useStateで管理することで、ReactがUIの変化を検知してre-renderします。

基本的な使い方

js
const [state, setState] = useState(初期値);
// state: 現在の値
// setState: 値を変更する関数

注意点

  • setStateを使わずにstateを直接変更してはいけない → 画面が更新されない
  • setStateは非同期 → 呼び出し直後にstateの値が変わらない場合がある
  • 配列やオブジェクトは新しい参照に置き換える必要がある(イミュータビリティ)
💻 カウンター + トグル + 入力フォームのサンプル
import { useState } from 'react';

// ===== カウンターコンポーネント =====
function Counter() {
  // 入力: 初期値 0
  const [count, setCount] = useState(0);

  // 処理: 増加/減少/初期化
  return (
    <div>
      <p>現在のカウント: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <button onClick={() => setCount(count - 1)}>-1</button>
      <button onClick={() => setCount(0)}>初期化</button>
    </div>
  );
}

// ===== トグルコンポーネント =====
function Toggle() {
  const [isOn, setIsOn] = useState(false);

  return (
    <button onClick={() => setIsOn(!isOn)}>
      {isOn ? "🟢 ON" : "⚫ OFF"}
    </button>
  );
}

// ===== 入力フォームコンポーネント =====
function NameForm() {
  const [name, setName] = useState("");

  // 出力: リアルタイム入力値表示
  return (
    <div>
      <input
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="名前を入力してください"
      />
      <p>{name ? `こんにちは、${name}さん!` : "名前を入力してください"}</p>
    </div>
  );
}
💻 2025年推奨パターン — 関数型アップデートとイミュータビリティの保証
// ✅ 良い例 - 関数型アップデート、遅延初期化、不変性
import { useState, useCallback } from 'react';
import { produce } from 'immer'; // 2025年標準不変性ライブラリ

interface User {
  id: number;
  name: string;
  settings: { theme: string; notifications: boolean; };
}

function GoodExample() {
  // 遅延初期化 - 関数として渡すと最初のレンダリング時にのみ実行
  const [count, setCount] = useState(() => {
    console.log('初期化は一度だけ実行されます');
    return expensiveCalculation();
  });
  
  const [user, setUser] = useState<User>(() => ({
    id: 1,
    name: 'John',
    settings: { theme: 'light', notifications: true }
  }));

  // 関数型アップデートでクロージャ問題を解決
  const handleMultipleClicks = useCallback(() => {
    setTimeout(() => {
      setCount(prevCount => prevCount + 1); // 常に最新の状態に基づく
      setCount(prevCount => prevCount + 1); // 正確に +2 される
    }, 1000);
  }, []);

  // immerを使用した不変性アップデート (2025年推奨)
  const toggleNotifications = useCallback(() => {
    setUser(prevUser => 
      produce(prevUser, draft => {
        draft.settings.notifications = !draft.settings.notifications;
      })
    );
  }, []);

  // またはスプレッド演算子方式
  const toggleTheme = useCallback(() => {
    setUser(prevUser => ({
      ...prevUser,
      settings: {
        ...prevUser.settings,
        theme: prevUser.settings.theme === 'light' ? 'dark' : 'light'
      }
    }));
  }, []);

  return (
    <div>
      <p>Count: {count}</p>
      <p>Theme: {user.settings.theme}</p>
      <p>Notifications: {user.settings.notifications ? 'On' : 'Off'}</p>
      <button onClick={handleMultipleClicks}>Add 2</button>
      <button onClick={toggleNotifications}>Toggle Notifications</button>
      <button onClick={toggleTheme}>Toggle Theme</button>
    </div>
  );
}

function expensiveCalculation(): number {
  return Array.from({length: 1000000}, (_, i) => i).reduce((a, b) => a + b, 0);
}

💡 ⚠️ よくある間違い

  • useStateの初期値に関数呼び出しの結果を直接渡してしまい、毎回のレンダリングで不要な計算が実行される
  • 前の状態に依存するアップデートで、関数型アップデートの代わりに現在の状態を直接参照してクロージャのバグを引き起こす
  • オブジェクトや配列のstateを直接ミューテートしてしまい、Reactが変化を検知できなくなる

💡 🎯 面接対策

Q: useStateで関数型アップデートが必要な場面と、その理由は?
Q: Reactにおける状態のイミュータビリティが重要な理由と、その実装方法は?

ヒント:関数型アップデートはクロージャの問題の解決と、前の状態に基づく安全なアップデートのために必要です。イミュータビリティはReactの浅い比較による最適化と予測可能な状態管理のために重要です。immerやスプレッド演算子などの具体的な実装方法にも触れてください。

⚛️ Reactパターン — useState

useStateをReactでどのように使うか、コードとともにステップごとに学びましょう。
1 📦 1. Stateの宣言
useStateでコンポーネント内部の状態を作成する
const [count, setCount] = useState(0);
2 👁️ 2. レンダリング
stateの値をJSXに表示する
return <h1>現在のカウント: {count}</h1>;
3 🔄 3. アップデート
setStateを呼び出す → re-renderをトリガーする
<button onClick={() => setCount(count + 1)}>+1</button>
4 💡 4. 基本原則
stateはイミュータブル — 直接変更せず、常にsetterを使う

✨ useState — カウンターを作る

コードを直接編集すると0.7秒後に自動で反映されます。React 18 + Babelでブラウザ上で即時実行されます。
🖥️ 実行結果 — レンダリングされたReactコンポーネント
✏️ React 코드 수정하기 (클릭해서 열기)
⚛️ React 18 + Babel Standalone — まず結果を確認し、エディタで自由に編集できます。

理解度チェッククイズ

setStateを使わずにstateを直接変更するとどうなりますか?
💡 stateを直接変更するとメモリ上の値は変わりますが、Reactは変化を検知できないため画面が更新されません。再レンダリングがトリガーされるのは、必ずsetState関数を通じて更新した場合のみです!
先に読むとよい概念: リストとkey
次のおすすめ: フォーム処理
useState - React