レッスン一覧に戻る

useEffectの基礎

レッスン 2

副作用処理を実行するuseEffectフックの基本的な使い方を学びます

推定学習時間: 25分

useEffectとは

useEffectは、Reactコンポーネントで「副作用(Side Effect)」を実行するためのフックです。

💡 副作用(Side Effect)とは?

副作用とは、コンポーネントのレンダリング以外で行われる処理のことです。 例えば、API呼び出し、タイマー設定、DOM操作、ログ出力などが副作用に当たります。

🎯 例えで理解しよう

🍽️ レストランの例: useEffectは「お客様が座った後に、注文を取って、料理を運ぶ」ようなものです。 画面に表示された後(コンポーネントがレンダリングされた後)に、必要な処理を実行します。

📞 電話の例: useEffectは「電話が繋がった後に、用件を伝える」ようなものです。 コンポーネントが表示された後に、外部との通信や処理を行います。

⏰ タイマーの例: useEffectは「時計が動き始めた後に、定期的に時刻を更新する」ようなものです。 コンポーネントが表示された後に、継続的な処理を開始します。

🍽️ 身近な例えで理解しよう:レストランの例

レストランの流れ

  1. 1. お客様が席に座る(コンポーネント表示)
  2. 2. ウェイターが注文を取る(useEffect実行)
  3. 3. 料理を運ぶ(副作用処理:API呼び出しなど)
  4. 4. お客様が帰る時に片付ける(クリーンアップ)

useEffectの流れ

  1. 1. コンポーネントが表示される(マウント)
  2. 2. useEffectが実行される
  3. 3. 副作用処理を実行(API、タイマーなど)
  4. 4. コンポーネント削除時にクリーンアップ

💡 useEffectは「お客様が座った後に注文を取り、料理を運ぶウェイター」のようなものです

✅ useEffectを使う理由

  • API呼び出し: データを外部サーバーから取得できます
  • タイマー処理: 定期的に何かを実行できます(例:時計、カウントダウン)
  • DOM操作: ブラウザのAPIを使って、ページタイトルを変更したり、スクロール位置を設定したりできます
  • イベントリスナー: ウィンドウのサイズ変更などのイベントを監視できます

基本的な使い方

useEffectの基本的な書き方を学びましょう。

📝 useEffectの基本構文

import { useEffect } from "react"; const MyComponent = () => { // useEffectの基本的な書き方 useEffect(() => { // ここに副作用の処理を書く console.log("コンポーネントが表示されました!"); }); return <div>Hello World</div>; };

1. useEffectのインポート: ReactからuseEffectをインポートします

2. useEffectの呼び出し: コンポーネント内でuseEffectを呼び出します

3. 処理の記述: 第1引数として、実行したい処理を関数で渡します

4. 実行タイミング: デフォルトでは、コンポーネントがレンダリングされるたびに実行されます

⚠️ 重要な注意点

  • 依存配列を指定しない場合、毎回のレンダリング後に実行されます(非推奨)
  • 多くの場合、依存配列を指定して、必要な時だけ実行するようにします

依存配列の基本

依存配列を使うことで、useEffectを実行するタイミングを制御できます。

📊 依存配列の3つのパターン比較

パターン1

依存配列なし

useEffect(() => { ... });
実行:
...
⚠️ 毎回実行

📰 例: 新聞配達員が毎日来る → 非推奨(パフォーマンス低下)

パターン2

空の配列 []

useEffect(() => { ... }, []);
実行:
✅ 1回だけ

🏠 例: 引っ越し時の一回だけの掃除 → API初期データ取得に最適

パターン3

依存配列あり [count]

useEffect(() => { ... }, [count]);
実行:
✅ 値が変わる時

🔔 例: ドアベルが鳴るたびに対応 → 状態に応じた処理に最適

パターン実行タイミング使用例
なし毎回のレンダリング後非推奨
[]マウント時のみ(1回)API初期データ取得、タイマー設定
[count]値が変わる時検索フィルター、状態に応じた処理

コンポーネントのライフサイクルとuseEffect

🎬 マウントコンポーネント表示useEffect実行🔄 更新状態変更クリーンアップ→再実行🎭 アンマウントコンポーネント削除クリーンアップ実行⏸️ 待機通常状態副作用実行中useEffectライフサイクル

🎭 身近な例えで理解しよう:劇場の例

🎬 マウント時 = 劇が始まる

カーテンが上がる(コンポーネント表示)→ 照明が点灯(useEffect実行)

🔄 更新時 = シーンが変わる

舞台セットが変わる → 前の道具を片付ける → 新しい準備

🎭 アンマウント時 = 劇が終わる

カーテンが下りる → 照明を消し、道具を片付ける

⏸️ 待機 = 通常状態

副作用処理が実行中で、次の状態変化を待っている

📝 空の依存配列 [](マウント時のみ実行)

useEffect(() => { // コンポーネントが初めて表示された時だけ実行される console.log("初めて表示されました!"); // 例:APIからデータを取得 fetch("/api/data") .then(response => response.json()) .then(data => console.log(data)); }, []); // ← 空の配列 []

💡 空の依存配列[]を指定すると、コンポーネントがマウントされた時(初めて表示された時)に1回だけ実行されます。

📝 依存配列に値を指定(値が変わった時だけ実行)

const [count, setCount] = useState(0); useEffect(() => { // countが変わった時だけ実行される console.log("countが変わりました:", count); // 例:countが変わるたびにAPIを呼び出す fetch(`/api/data?count=${count}`) .then(response => response.json()) .then(data => console.log(data)); }, [count]); // ← countを指定

💡 依存配列にcountを指定すると、countの値が変わるたびに実行されます。

✅ 依存配列の使い分け

  • 空の配列 []: コンポーネントが表示された時だけ実行(API初期データ取得など)
  • 値の指定 [count]: 指定した値が変わった時だけ実行(フィルター、検索など)
  • 複数の値 [count, name]: いずれかの値が変わった時に実行

クリーンアップ関数

useEffectは、クリーンアップ関数を返すことができます。 この関数は、useEffectが再実行される前や、コンポーネントが削除される時に実行されます。

🧹 クリーンアップ関数の動作フロー

1

useEffect実行

副作用処理を開始(タイマー設定、API呼び出しなど)

2

副作用処理実行中

const timer = setInterval(...)
3

クリーンアップ関数を返す

return () => { clearInterval(timer); };

⚡ トリガー: 状態変更 or アンマウント

4

⚠️ クリーンアップ実行

前回の処理をクリーンアップ

  • clearInterval(timer)
  • AbortController.abort()
  • イベントリスナー削除
5

新しいuseEffect実行(オプション)

値が変更された場合のみ、新しい処理を実行

🧹 身近な例えで理解しよう

🏠 お部屋の例え

クリーンアップ関数は「引っ越す前に部屋を片付ける」ようなものです。新しい処理を始める前や、コンポーネントが削除される時に、前の処理の後始末をします。

⏰ アラーム時計の例え

アラームをセットする(useEffect実行)→ 新しいアラームをセットする前に、前のアラームをキャンセルする(クリーンアップ)。実行順序: 1. 前回のクリーンアップ実行 → 2. 新しいuseEffect実行(値が変更された場合のみ)

用途: タイマーのクリア、APIリクエストのキャンセル、イベントリスナーの削除など、リソースの適切な解放を行います。

📝 クリーンアップ関数の基本構文

useEffect(() => { // 副作用処理 const timer = setInterval(() => { console.log("1秒ごとに実行"); }, 1000); // クリーンアップ関数を返す return () => { clearInterval(timer); // タイマーをクリア }; }, []);

💡 クリーンアップ関数は、副作用処理で作成したリソース(タイマー、イベントリスナーなど)を適切に解放するために使用します。

⚠️ クリーンアップが必要な理由

  • メモリリークの防止: タイマーやイベントリスナーを削除しないと、メモリリークが発生する可能性があります
  • リソースの適切な解放: 不要になったリソースを適切に解放することで、アプリケーションのパフォーマンスが向上します
  • 予期しない動作の防止: コンポーネントが削除された後も処理が実行され続けることを防ぎます

useStateとuseEffectの連携

useStateuseEffectは、一緒に使うことで非常に強力な組み合わせになります。

🔄 useStateとuseEffectの連携図解

💾

useState

状態を管理

const [count, setCount] = useState(0);
📊

状態: count

初期値: 0

0
🔥

状態変更

setCount(5)

count: 0 → 5

useEffect

自動実行

useEffect(() => { ... }, [count]);
🎯

副作用処理実行

countの新しい値(5)を使用

• API呼び出し

• ページタイトル更新

• DOM操作

🔄 身近な例えで理解しよう:温度計とエアコンの例

🌡️ 1. 状態の定義 = 温度計を設置

useStateで状態(count)を定義。これは「温度計」のようなもので、現在の温度(値)を表示します。

🔥 2. 状態の変更 = 温度が上がる

setCount(5)でcountが0から5に変更。これは「温度が25度から30度に上がった」ようなものです。

👁️ 3. useEffectの監視 = 温度計を見ているセンサー

useEffectは依存配列にcountを含めているため、countが変わると自動的に再実行されます。

❄️ 4. 副作用の実行 = エアコンが自動で動く

useEffect内でcountの新しい値(5)を使って、API呼び出しやDOM操作などの副作用処理を実行します。

📝 基本的な連携パターン

const MyComponent = () => { const [count, setCount] = useState(0); // countが変わるたびに実行 useEffect(() => { console.log("countが変わりました:", count); // countの値を使って何か処理 document.title = `カウント: ${count}`; }, [count]); // ← countを依存配列に含める return ( <div> <p>カウント: {count{}</p> <button onClick={() => setCount(count + 1)}>+1</button> </div> ); };

💡 useStateで状態を管理し、useEffectでその状態に応じた副作用処理を実行する、これが最も一般的なパターンです。

⚠️ 重要な注意点

  • 依存配列に必ず含める: useEffect内で使用している状態やpropsは、依存配列に必ず含める必要があります
  • 古い値を参照する可能性: 依存配列に含めないと、古い値が参照される可能性があります
  • ESLintの警告: React HooksのESLintルールが、依存配列の不足を警告してくれます

実践的な例

実際によく使われるuseEffectの使い方を見てみましょう。

📝 例1: ページタイトルを変更する

const MyComponent = () => { const [count, setCount] = useState(0); useEffect(() => { // ページタイトルを変更 document.title = `カウント: ${count}`; }, [count]); // countが変わるたびに実行 return ( <div> <p>カウント: {count{}</p> <button onClick={() => setCount(count + 1)}>+1</button> </div> ); };

💡 ブラウザのタブに表示されるタイトルを、状態に応じて動的に変更できます。

📝 例2: タイマーを設定する

const Timer = () => { const [seconds, setSeconds] = useState(0); useEffect(() => { // タイマーを設定(1秒ごとに実行) const interval = setInterval(() => { setSeconds(prev => prev + 1); }, 1000); // クリーンアップ関数(後で説明) return () => { clearInterval(interval); }; }, []); // マウント時のみ実行 return <p>経過時間: {seconds{}秒</p>; };

💡 コンポーネントが表示されたときにタイマーを開始し、削除されたときにタイマーをクリアします。

📝 例3: APIからデータを取得する

🌐 API呼び出しの流れ図解

1
📞

コンポーネントマウント

コンポーネントが画面に表示されると、useEffectが自動的に実行されます

useEffect(() => { ... }, []);
2

ローディング状態

API呼び出し前に、ローディング状態をtrueに設定して、ユーザーに読み込み中であることを伝えます

setLoading(true);
3
🌐

API呼び出し

fetchを使ってサーバーからデータを取得します

fetch("/api/user") .then(...)
4

データ取得成功

取得したデータを状態に保存し、ローディング状態をfalseに戻します

setUser(data); setLoading(false);
5
📺

画面に表示

状態が更新されると、コンポーネントが自動的に再描画され、データが画面に表示されます

🍕 身近な例えで理解しよう:ピザ配達の例

📞 1. ピザを注文する

電話をかける(コンポーネント表示)→ ピザ屋に注文を入れる

⏳ 2. 「お待ちください」と伝える

「ただいまお作りしています」と伝える(ローディング状態)

🚗 3. ピザを作って配達する

ピザ屋でピザを作る(サーバー処理)→ 配達する(API呼び出し)

🍽️ 4. ピザを食べる

ピザを受け取る(データ取得)→ 食べる(画面に表示)

const UserProfile = () => { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { // ローディング状態をtrueに設定 setLoading(true); // APIからデータを取得 fetch("/api/user") .then(response => response.json()) .then(data => { setUser(data); setLoading(false); }) .catch(error => { console.error("エラー:", error); setLoading(false); }); }, []); // マウント時のみ実行 if (loading) return <p>読み込み中...</p>; if (!user) return <p>ユーザーが見つかりません</p>; return <div>こんにちは、{user.name{}さん</div>; };

💡 この例の流れ

  1. コンポーネントがマウントされると、useEffectが実行されます
  2. ローディング状態をtrueに設定して、ユーザーに読み込み中であることを伝えます
  3. fetch APIを使ってサーバーからデータを取得します
  4. データ取得成功時に、状態を更新してローディングをfalseにします
  5. 状態が更新されると、コンポーネントが再描画されて、データが画面に表示されます

まとめ

🎯 覚えておきたいポイント

  1. useEffectは副作用処理を実行するためのフックです
  2. useEffect(() => { ... }, [依存配列]);の形式で使用します
  3. 依存配列が[](空)の場合、マウント時のみ実行されます
  4. 依存配列に値を指定すると、その値が変わった時だけ実行されます
  5. 依存配列を指定しない場合、毎回のレンダリング後に実行されます(非推奨)