プログラミング15分で読める

React Hooks入門 - useState、useEffectの基本を理解する

Reactの状態管理に欠かせないHooksの基本を、実践的なコード例とともにわかりやすく解説します。

React Hooksとは

Hooksは、React 16.8(2019年)で導入された機能で、関数コンポーネントで状態管理やライフサイクル処理を行うための仕組みです。

Hooksのメリット:

  • クラスコンポーネントより簡潔で読みやすいコード
  • ロジックの再利用がしやすい(カスタムHooks)
  • thisの扱いを気にする必要がない
  • コンポーネントの責務が明確になる

useState - 状態管理の基本

useStateは、コンポーネント内で状態(state)を管理するための最も基本的なHookです。

基本構文

React (TypeScript)
import { useState } from 'react';

function Counter() {
  // [現在の値, 更新関数] = useState(初期値)
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>カウント: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        増やす
      </button>
    </div>
  );
}

例1: テキスト入力フォーム

React
function NameForm() {
  const [name, setName] = useState('');

  const handleChange = (e) => {
    setName(e.target.value);
  };

  return (
    <div>
      <input
        type="text"
        value={name}
        onChange={handleChange}
        placeholder="名前を入力"
      />
      <p>こんにちは、{name || 'ゲスト'}さん!</p>
    </div>
  );
}

例2: オブジェクトの状態管理

React
function UserProfile() {
  const [user, setUser] = useState({
    name: '',
    email: '',
    age: 0
  });

  // オブジェクトの一部だけ更新する場合はスプレッド構文を使う
  const updateName = (newName) => {
    setUser({ ...user, name: newName });
  };

  return (
    <input
      value={user.name}
      onChange={(e) => updateName(e.target.value)}
    />
  );
}

注意:オブジェクトや配列の状態を更新する場合は、必ず新しいオブジェクト/配列を作成してください。直接変更しても再レンダリングされません。

例3: 配列の状態管理

React
function TodoList() {
  const [todos, setTodos] = useState([]);
  const [input, setInput] = useState('');

  // 追加
  const addTodo = () => {
    if (input.trim()) {
      setTodos([...todos, { id: Date.now(), text: input }]);
      setInput('');
    }
  };

  // 削除
  const removeTodo = (id) => {
    setTodos(todos.filter(todo => todo.id !== id));
  };

  return (
    <div>
      <input value={input} onChange={(e) => setInput(e.target.value)} />
      <button onClick={addTodo}>追加</button>
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>
            {todo.text}
            <button onClick={() => removeTodo(todo.id)}>削除</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

useEffect - 副作用の処理

useEffectは、コンポーネントのレンダリング後に実行される処理(副作用)を定義するHookです。

基本構文

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

function Example() {
  const [count, setCount] = useState(0);

  // 依存配列なし:毎回のレンダリング後に実行
  useEffect(() => {
    console.log('レンダリングされました');
  });

  // 空の依存配列:マウント時のみ実行
  useEffect(() => {
    console.log('コンポーネントがマウントされました');
  }, []);

  // 依存配列あり:countが変化した時に実行
  useEffect(() => {
    console.log('countが変更されました:', count);
  }, [count]);

  return <p>{count}</p>;
}
🔄

依存配列なし

毎回のレンダリング後に実行

🚀

空の配列 []

マウント時に1回だけ実行

👁️

[value]

valueが変化した時に実行

🧹

return関数

クリーンアップ処理

例1: APIからデータを取得

React
function UserList() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchUsers = async () => {
      try {
        const response = await fetch('https://api.example.com/users');
        const data = await response.json();
        setUsers(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchUsers();
  }, []); // 空の配列:マウント時に1回だけ実行

  if (loading) return <p>読み込み中...</p>;
  if (error) return <p>エラー: {error}</p>;

  return (
    <ul>
      {users.map(user => <li key={user.id}>{user.name}</li>)}
    </ul>
  );
}

例2: クリーンアップ処理(タイマー)

React
function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    // タイマーを開始
    const intervalId = setInterval(() => {
      setSeconds(prev => prev + 1);
    }, 1000);

    // クリーンアップ:コンポーネントがアンマウントされる時にタイマーを解除
    return () => {
      clearInterval(intervalId);
    };
  }, []); // マウント時に1回だけ実行

  return <p>経過時間: {seconds}秒</p>;
}

クリーンアップが必要なケース:

  • setInterval / setTimeout のクリア
  • イベントリスナーの解除
  • WebSocket接続の切断
  • 外部ライブラリのインスタンス破棄

Hooksのルール

Hooksには守るべき2つの重要なルールがあります。

ルール1: トップレベルでのみ呼び出す

条件分岐やループの中でHooksを呼び出さないでください。

悪い例 ❌
function BadExample({ isLoggedIn }) {
  // ❌ 条件分岐の中でHooksを呼んではいけない
  if (isLoggedIn) {
    const [user, setUser] = useState(null);
  }
}
良い例 ✅
function GoodExample({ isLoggedIn }) {
  // ✅ 常にトップレベルで呼び出す
  const [user, setUser] = useState(null);

  // 条件分岐はHooksの後で行う
  if (!isLoggedIn) {
    return <p>ログインしてください</p>;
  }
}

ルール2: React関数内でのみ呼び出す

Hooksは関数コンポーネントかカスタムHooksの中でのみ使用できます。

実践的なパターン

パターン1: フォーム入力のバリデーション

React
function LoginForm() {
  const [email, setEmail] = useState('');
  const [error, setError] = useState('');

  // emailが変更されるたびにバリデーション
  useEffect(() => {
    if (email && !email.includes('@')) {
      setError('有効なメールアドレスを入力してください');
    } else {
      setError('');
    }
  }, [email]);

  return (
    <div>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
      />
      {error && <p style={{ color: 'red' }}>{error}</p>}
    </div>
  );
}

その他の便利なHooks

🌐

useContext

グローバルな状態を共有

🔧

useReducer

複雑な状態管理に

📌

useRef

DOM参照や値の保持

useMemo

計算結果のメモ化

まとめ

useStateuseEffectはReact開発の基盤となるHooksです。この2つをしっかり理解すれば、ほとんどのReactアプリケーションを構築できます。

次のステップ:基本をマスターしたら、useContextでグローバル状態管理、useReducerで複雑な状態管理、カスタムHooksの作成に進みましょう。

H
honualohak編集部

テクノロジーで人々の日常をより便利に。AI、プログラミング、Web開発に関する情報を発信しています。