AI時代のReact入門 第8回:状態管理①(useStateの基礎)

「ボタンをクリックしたら表示を切り替えたい…」 「入力フォームの値を保持したい…」

静的なコンポーネントは作れるようになったけれど、ユーザーの操作に応じて画面を変化させたいと思ったことはありませんか?実は、ReactのuseStateを使えば、そんな動的なUIを簡単に実装できるんです。

今回は、React Hooksの中でも最も基本的な「useState」について、実践的なコード例を交えながら解説していきます。この記事を読めば、インタラクティブなUIコンポーネントが作れるようになり、Reactの醍醐味を味わえるはずです。

Reactの状態管理:useStateの基礎を理解しよう

「ボタンをクリックしたら表示を切り替えたい…」 「入力フォームの値を保持したい…」

静的なコンポーネントは作れるようになったけれど、ユーザーの操作に応じて画面を変化させたいと思ったことはありませんか?実は、ReactのuseStateを使えば、そんな動的なUIを簡単に実装できるんです。

今回は、React Hooksの中でも最も基本的な「useState」について、実践的なコード例を交えながら解説していきます。この記事を読めば、インタラクティブなUIコンポーネントが作れるようになり、Reactの醍醐味を味わえるはずです。

useStateとは何か

useStateは、Reactコンポーネントに「状態」を追加するためのHookです。状態とは、時間とともに変化する可能性のあるデータのことです。

たとえば:

  • ボタンが押されているかどうか
  • フォームに入力された文字列
  • 商品の数量
  • モーダルが開いているかどうか

これらはすべて「状態」として管理できます。

useStateの基本的な使い方

useStateは以下のような形で使います:

jsx
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>カウント: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        増やす
      </button>
    </div>
  );
}

ここで重要なポイントは:

  1. useStatereactからインポート
  2. useState(初期値)で状態を初期化
  3. 配列の分割代入で状態変数と更新関数を取得
  4. 更新関数を使って状態を変更

実践的なuseState活用例

1. シンプルなトグルボタン

jsx
import React, { useState } from 'react';

const ToggleButton = () => {
  const [isOn, setIsOn] = useState(false);

  const toggleStyles = {
    on: 'bg-blue-500 text-white',
    off: 'bg-gray-300 text-gray-700'
  };

  return (
    <div className="space-y-4">
      <button
        className={`px-4 py-2 rounded ${isOn ? toggleStyles.on : toggleStyles.off}`}
        onClick={() => setIsOn(!isOn)}
      >
        {isOn ? 'ON' : 'OFF'}
      </button>
      <p className="text-gray-600">
        現在の状態: {isOn ? 'オン' : 'オフ'}
      </p>
    </div>
  );
};

export default ToggleButton;

2. 入力フォームの実装

jsx
import React, { useState } from 'react';

const InputForm = () => {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [message, setMessage] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    // ここで入力値を使った処理を行う
    alert(`名前: ${name}\nメール: ${email}\nメッセージ: ${message}`);
  };

  return (
    <form onSubmit={handleSubmit} className="space-y-4">
      <div>
        <label className="block text-sm font-medium text-gray-700">お名前</label>
        <input
          type="text"
          value={name}
          onChange={(e) => setName(e.target.value)}
          className="mt-1 block w-full rounded border-gray-300 shadow-sm"
        />
      </div>
      
      <div>
        <label className="block text-sm font-medium text-gray-700">メールアドレス</label>
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          className="mt-1 block w-full rounded border-gray-300 shadow-sm"
        />
      </div>
      
      <div>
        <label className="block text-sm font-medium text-gray-700">メッセージ</label>
        <textarea
          value={message}
          onChange={(e) => setMessage(e.target.value)}
          className="mt-1 block w-full rounded border-gray-300 shadow-sm"
          rows="4"
        />
      </div>
      
      <button
        type="submit"
        className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
      >
        送信
      </button>
    </form>
  );
};

export default InputForm;

よくある間違いと解決方法

1. 状態を直接変更しようとする

jsx
// ❌ 誤った使い方
const [count, setCount] = useState(0);
count = count + 1;  // 直接代入はNG

// ✅ 正しい使い方
const [count, setCount] = useState(0);
setCount(count + 1);  // 更新関数を使う

2. 前の状態に基づく更新

jsx
// ❌ 更新のタイミングによっては期待通りに動かないことも
setCount(count + 1);

// ✅ 前の状態を使って更新する場合は関数を渡す
setCount(prevCount => prevCount + 1);

3. オブジェクトの状態更新

jsx
// ❌ オブジェクトの一部だけを更新しようとする
const [user, setUser] = useState({ name: '', email: '' });
setUser({ name: 'John' });  // emailが消えてしまう

// ✅ スプレッド構文で既存の値を保持
setUser(prevUser => ({ ...prevUser, name: 'John' }));

useStateのベストプラクティス

1. 明確な命名

jsx
// ❌ 曖昧な命名
const [x, setX] = useState(false);

// ✅ 目的が明確な命名
const [isVisible, setIsVisible] = useState(false);
const [userName, setUserName] = useState('');

2. 適切な粒度での状態管理

jsx
// ❌ 1つのオブジェクトにまとめすぎ
const [form, setForm] = useState({
  name: '',
  email: '',
  age: '',
  address: ''
});

// ✅ 適切に分割
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [age, setAge] = useState('');
const [address, setAddress] = useState('');

まとめ

useStateを使うことで、動的なUIを実装できることがわかりました。重要なポイントを復習しましょう:

  • useStateは状態を管理するためのHook
  • 状態変数と更新関数がセットで提供される
  • 状態の更新は必ず更新関数を使う
  • 前の状態に基づく更新は関数を使う
  • 適切な粒度で状態を分割する

次回は、useStateのより実践的な使い方について学んでいきます。

複数の状態を組み合わせた複雑なフォームの実装や、パフォーマンスを考慮した状態管理など、実務で使えるテクニックを解説していきますのでお楽しみに!