ReactのuseEffectでイベントトリガーを実現する方法を徹底解説

Reactは、コンポーネントベースの設計と仮想DOMによって、効率的なUI構築を可能にするJavaScriptライブラリです。その中でもuseEffectフックは、副作用の管理を簡素化する重要な役割を果たします。副作用とは、データの取得、DOMの更新、イベントリスナーの登録など、コンポーネントのレンダリング以外で行われる処理を指します。本記事では、useEffectを用いて特定のイベントが発生した際にアクションをトリガーする方法を中心に解説します。初心者でも理解できる基本概念から、実践的なコード例や応用例まで網羅し、Reactアプリケーションにおけるイベント処理のポイントを押さえます。

目次
  1. useEffectの基本概念
    1. 主な特徴
    2. 基本構文
    3. 使用例
    4. 適用範囲
  2. useEffectでイベントリスナーを設定する方法
    1. 基本構文
    2. DOMイベントの設定例
    3. 依存配列の活用
    4. 注意点
  3. クリーンアップの重要性と方法
    1. クリーンアップが必要な理由
    2. 基本構文
    3. 具体例
    4. 依存配列とクリーンアップ
    5. 注意点
  4. 依存配列の使い方と注意点
    1. 依存配列とは
    2. 依存配列の使い方
    3. 依存配列の注意点
    4. 依存配列を省略するリスク
    5. まとめ
  5. カスタムイベントのトリガー方法
    1. カスタムイベントの基本概念
    2. カスタムイベントの実装例
    3. カスタムイベントを使用したコンポーネント間通信
    4. カスタムイベントの応用例
    5. 注意点
  6. よくある落とし穴とその回避策
    1. 落とし穴1: 無限ループ
    2. 落とし穴2: 依存配列の不完全な設定
    3. 落とし穴3: 不適切なクリーンアップ
    4. 落とし穴4: 過剰な副作用の実行
    5. 落とし穴5: 非同期処理の競合
    6. まとめ
  7. 実践例:スクロールイベントのハンドリング
    1. 基本的なスクロールイベントの設定
    2. 特定のスクロール位置でアクションをトリガー
    3. スクロール方向の判定
    4. パフォーマンス向上のための最適化
    5. まとめ
  8. 応用例:APIデータ取得時のイベント処理
    1. API呼び出しをトリガーするイベントの設定
    2. スクロール位置に応じたAPIデータの取得
    3. フォーム入力とAPI呼び出しの組み合わせ
    4. APIとカスタムイベントの連携
    5. まとめ
  9. まとめ

useEffectの基本概念


ReactのuseEffectフックは、コンポーネントがレンダリングされた後に副作用を実行するための仕組みを提供します。useEffectは、レンダリングごとに実行されるため、データの取得やイベントリスナーの登録、DOMの変更など、さまざまなタスクに利用されます。

主な特徴


useEffectの基本的な挙動は以下の通りです。

  • 副作用の実行タイミング: コンポーネントがマウントされた後、または依存関係が更新された後に実行されます。
  • 依存配列の指定: 必要に応じて実行する条件を制御可能です。依存配列を空にすると、初回のレンダリング時にのみ実行されます。

基本構文


以下はuseEffectの基本的な構文です。

import React, { useEffect } from 'react';

function ExampleComponent() {
  useEffect(() => {
    // 副作用をここに記述
    console.log("Component mounted or updated");

    // クリーンアップ関数(オプション)
    return () => {
      console.log("Component unmounted or before update");
    };
  }, []); // 依存配列

  return <div>Example Component</div>;
}

使用例


例えば、APIからデータを取得する場合、useEffectを次のように活用できます。

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

function DataFetcher() {
  const [data, setData] = useState(null);

  useEffect(() => {
    async function fetchData() {
      const response = await fetch("https://api.example.com/data");
      const result = await response.json();
      setData(result);
    }

    fetchData();
  }, []); // 初回マウント時のみ実行

  return <div>{data ? JSON.stringify(data) : "Loading..."}</div>;
}

適用範囲


useEffectは、以下のようなユースケースで特に役立ちます。

  • DOM操作やイベントリスナーの登録
  • APIコールによるデータ取得
  • タイマーやアニメーションの設定

これにより、Reactコンポーネントのライフサイクルに応じた柔軟な副作用の管理が可能になります。

useEffectでイベントリスナーを設定する方法

ReactのuseEffectを利用すると、DOMイベントを監視し、特定の条件で処理を実行するイベントリスナーを簡単に設定できます。これにより、ユーザーの操作やブラウザの動作を検知して、動的なアプリケーションを実現できます。

基本構文


イベントリスナーを設定する際の基本的な構文は以下の通りです。

import React, { useEffect } from 'react';

function EventListenerComponent() {
  useEffect(() => {
    const handleEvent = () => {
      console.log("Event triggered");
    };

    // イベントリスナーの登録
    window.addEventListener("click", handleEvent);

    // クリーンアップ関数でリスナーを解除
    return () => {
      window.removeEventListener("click", handleEvent);
    };
  }, []); // 空の依存配列で初回のみ設定

  return <div>Click anywhere to trigger the event</div>;
}

DOMイベントの設定例

1. キーボード入力イベントの監視

キーボード操作を検知して特定の処理を行う例です。

useEffect(() => {
  const handleKeyPress = (event) => {
    if (event.key === "Enter") {
      console.log("Enter key pressed");
    }
  };

  window.addEventListener("keydown", handleKeyPress);

  return () => {
    window.removeEventListener("keydown", handleKeyPress);
  };
}, []); // 初回のみ設定

2. ウィンドウのリサイズイベントの監視

画面サイズの変更に応じてレイアウトを調整する場合などに使用します。

useEffect(() => {
  const handleResize = () => {
    console.log(`Window size: ${window.innerWidth}x${window.innerHeight}`);
  };

  window.addEventListener("resize", handleResize);

  return () => {
    window.removeEventListener("resize", handleResize);
  };
}, []);

依存配列の活用


イベントリスナーの設定を特定の状態に応じて更新したい場合は、依存配列を使用します。例えば、監視するイベントの種類を状態として管理する場合:

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

function DynamicEventListener() {
  const [eventType, setEventType] = useState("click");

  useEffect(() => {
    const handleEvent = () => {
      console.log(`${eventType} event triggered`);
    };

    window.addEventListener(eventType, handleEvent);

    return () => {
      window.removeEventListener(eventType, handleEvent);
    };
  }, [eventType]); // eventTypeが変更されるたびに再設定

  return (
    <div>
      <button onClick={() => setEventType("mousemove")}>Switch to Mousemove</button>
    </div>
  );
}

注意点

  • クリーンアップの徹底: イベントリスナーを解除しないと、メモリリークや予期しない動作が発生する可能性があります。
  • 依存配列の設定: 適切な依存関係を指定することで、不要な再レンダリングを防ぎます。

このように、useEffectを活用してイベントリスナーを柔軟に管理することで、Reactアプリケーションの機能性を向上させることができます。

クリーンアップの重要性と方法

ReactのuseEffectを使用する際に、クリーンアップ処理は非常に重要です。クリーンアップを適切に実装することで、メモリリークや不要なイベントリスナーの重複を防ぎ、アプリケーションの安定性を保つことができます。

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


以下のようなシナリオでは、クリーンアップが不可欠です:

  • イベントリスナーの重複登録: 複数回のレンダリングで同じリスナーが登録されると、意図しない動作やパフォーマンス低下を引き起こします。
  • タイマーやAPIリクエストのリセット: セットしたタイマーや未完了のAPIリクエストが原因でバグが発生する場合があります。
  • メモリリーク: DOM要素や他のリソースが適切に解放されないと、メモリ消費が増大します。

基本構文


クリーンアップ処理は、useEffectの戻り値として関数を返すことで実現します。この関数は、次のレンダリングの直前またはコンポーネントのアンマウント時に実行されます。

useEffect(() => {
  const handleEvent = () => {
    console.log("Event triggered");
  };

  window.addEventListener("click", handleEvent);

  // クリーンアップ関数を返す
  return () => {
    window.removeEventListener("click", handleEvent);
  };
}, []);

具体例

1. イベントリスナーの解除

イベントリスナーを解除して、不要なトリガーを防ぐ例です。

useEffect(() => {
  const handleScroll = () => {
    console.log("Scroll event detected");
  };

  window.addEventListener("scroll", handleScroll);

  return () => {
    window.removeEventListener("scroll", handleScroll);
  };
}, []);

2. タイマーのクリア

setTimeoutsetIntervalを使用した場合、コンポーネントがアンマウントされるときにタイマーを解除します。

useEffect(() => {
  const timer = setInterval(() => {
    console.log("Interval triggered");
  }, 1000);

  return () => {
    clearInterval(timer);
  };
}, []);

3. WebSocketやAPI接続のクリーンアップ

WebSocketの接続を解除する場合の例です。

useEffect(() => {
  const socket = new WebSocket("wss://example.com/socket");

  socket.onmessage = (event) => {
    console.log("Message received:", event.data);
  };

  return () => {
    socket.close();
  };
}, []);

依存配列とクリーンアップ


依存配列を指定した場合、クリーンアップは依存する値が変更されるたびに実行されます。

useEffect(() => {
  const handleResize = () => {
    console.log("Window resized");
  };

  window.addEventListener("resize", handleResize);

  return () => {
    window.removeEventListener("resize", handleResize);
  };
}, [/* 依存配列に値を指定 */]);

注意点

  • クリーンアップを忘れない: useEffectを使用する場合、特にイベントリスナーやタイマーを設定したときは必ずクリーンアップを実装しましょう。
  • 依存配列の最適化: 不適切な依存配列は、不要なクリーンアップと再登録を引き起こします。

クリーンアップ処理は、アプリケーションのリソース管理とパフォーマンスの鍵です。適切な実装により、スムーズなユーザー体験を提供できます。

依存配列の使い方と注意点

ReactのuseEffectフックにおける依存配列は、副作用がどのタイミングで再実行されるかを制御する重要な要素です。正しく依存配列を設定することで、効率的で正確な動作を保証できますが、不適切な設定はパフォーマンスの低下やバグの原因になります。

依存配列とは


useEffectは、第2引数として依存配列を取ります。この配列に指定した値が変化するたびにuseEffectが再実行されます。

useEffect(() => {
  console.log("Effect executed");
}, [dependency1, dependency2]);

動作例

  • 空の依存配列 []: 初回のレンダリング時にのみ実行されます。
  • 指定なし: コンポーネントのレンダリングごとに実行されます(推奨されません)。
  • 値を指定: 指定した値が変更されるたびに実行されます。

依存配列の使い方

1. 特定の値に依存させる

依存配列を使用して、副作用を特定の値に基づいて再実行するように制御します。

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

useEffect(() => {
  console.log(`Count changed to ${count}`);
}, [count]); // countが変更されるたびに実行

2. APIコールの最適化

データを取得する副作用を、必要な場合にのみ実行します。

const [userId, setUserId] = useState(1);
const [data, setData] = useState(null);

useEffect(() => {
  async function fetchData() {
    const response = await fetch(`https://api.example.com/user/${userId}`);
    const result = await response.json();
    setData(result);
  }

  fetchData();
}, [userId]); // userIdが変更されたときだけデータを再取得

依存配列の注意点

1. すべての依存関係を指定する

Reactは、useEffect内で使用されるすべての変数を依存配列に含めることを推奨しています。これにより、副作用が期待通りに実行されます。

useEffect(() => {
  console.log(`Value: ${value}`); // valueを依存配列に追加
}, [value]);

2. 関数やオブジェクトの依存関係

関数やオブジェクトは、再レンダリングごとに新しい参照として扱われるため、依存関係が変化したと認識されます。この場合はuseCallbackuseMemoを活用して解決します。

const handleClick = useCallback(() => {
  console.log("Button clicked");
}, []); // メモ化された関数を使用

useEffect(() => {
  console.log("Effect triggered");
}, [handleClick]); // handleClickを依存配列に追加

3. 空の依存配列を適切に使う

空の依存配列は初回のみ実行する副作用に便利ですが、コンポーネントの状態や外部の値を使用する場合は注意が必要です。適切に依存配列を設定しないと、古い値が使われてしまう可能性があります。

依存配列を省略するリスク


依存配列を省略すると、次のような問題が発生する可能性があります:

  • 無限ループ: レンダリングごとにuseEffectが実行されるため、パフォーマンスが著しく低下します。
  • 不整合な状態: 使用している変数や関数が最新ではない場合、予期しない動作を引き起こします。
useEffect(() => {
  console.log(`Dependency omitted: ${value}`);
}); // 無限ループや不整合のリスク

まとめ

  • 必要な値を正確に依存配列に含める: useEffect内で使用するすべての変数や関数を依存配列に追加します。
  • useCallbackuseMemoで最適化: 依存関係を安定化させ、副作用の頻度を制御します。
  • デバッグを活用: eslint-plugin-react-hooksを使用して依存配列の設定ミスを防ぎます。

正確に依存配列を設定することで、Reactアプリケーションのパフォーマンスと信頼性を向上させることができます。

カスタムイベントのトリガー方法

Reactでは、標準的なDOMイベントだけでなく、独自のカスタムイベントを作成し、useEffectを使用してそれに応答することも可能です。これにより、コンポーネント間で独自のアクションをトリガーし、柔軟で拡張性の高いアプリケーションを構築できます。

カスタムイベントの基本概念


カスタムイベントは、JavaScriptのCustomEventを使用して作成します。以下の2つのステップで実現できます:

  1. カスタムイベントを定義して発行する
  2. イベントリスナーを設定してそのイベントを監視する

カスタムイベントの実装例

1. イベントの作成と発行

以下の例では、customEventという名前のイベントを発行します。

useEffect(() => {
  const triggerCustomEvent = () => {
    const event = new CustomEvent("customEvent", {
      detail: { message: "Hello from custom event!" },
    });
    window.dispatchEvent(event); // イベントを発行
  };

  triggerCustomEvent();
}, []);

2. イベントリスナーの設定

カスタムイベントを監視して処理を実行します。

useEffect(() => {
  const handleCustomEvent = (event) => {
    console.log("Custom Event Triggered:", event.detail.message);
  };

  // カスタムイベントのリスナーを登録
  window.addEventListener("customEvent", handleCustomEvent);

  return () => {
    // クリーンアップでリスナーを解除
    window.removeEventListener("customEvent", handleCustomEvent);
  };
}, []);

カスタムイベントを使用したコンポーネント間通信

複数のコンポーネント間でカスタムイベントを利用して通信する例を示します。

1. イベントの発行元コンポーネント

function EventDispatcher() {
  const triggerEvent = () => {
    const event = new CustomEvent("notify", {
      detail: { content: "Data sent from EventDispatcher!" },
    });
    window.dispatchEvent(event);
  };

  return <button onClick={triggerEvent}>Send Custom Event</button>;
}

2. イベントの受信側コンポーネント

function EventListener() {
  useEffect(() => {
    const handleNotifyEvent = (event) => {
      console.log("Received message:", event.detail.content);
    };

    window.addEventListener("notify", handleNotifyEvent);

    return () => {
      window.removeEventListener("notify", handleNotifyEvent);
    };
  }, []);

  return <div>Listening for custom events...</div>;
}

3. コンポーネントを統合

カスタムイベントを使用して、独立したコンポーネント間でのデータ共有や通知を実現します。

function App() {
  return (
    <div>
      <EventDispatcher />
      <EventListener />
    </div>
  );
}

カスタムイベントの応用例

1. 状態変更通知

グローバルな状態管理の代替として、特定の状態が変化したときにイベントを発行して、他のコンポーネントがその変化に応答できます。

2. 非同期データ取得の完了通知

APIからのデータ取得が完了したときにカスタムイベントをトリガーし、UIを更新します。

useEffect(() => {
  const fetchData = async () => {
    const data = await fetch("https://api.example.com/data").then((res) => res.json());
    const event = new CustomEvent("dataFetched", { detail: { data } });
    window.dispatchEvent(event);
  };

  fetchData();
}, []);

3. モジュール間の連携

モジュール化されたアプリケーションにおいて、カスタムイベントを使用してモジュール間の緩やかな連携を実現します。

注意点

  • 名前の一意性を確保: カスタムイベントの名前は、他のイベントと競合しないように一意にします。
  • クリーンアップの実装: 必ずリスナーを解除して、メモリリークや不要なイベントトリガーを防ぎます。
  • スコープを考慮: 必要に応じて、グローバルスコープ(window)ではなく、ローカルなカスタムDOMノードにイベントをバインドします。

カスタムイベントは、Reactアプリケーションでの柔軟なイベント処理を可能にし、複雑なシナリオにも対応できる強力なツールです。

よくある落とし穴とその回避策

useEffectを使用する際、初心者が陥りやすい落とし穴がいくつかあります。不適切な使用は、アプリケーションの動作不良やパフォーマンスの低下につながります。本セクションでは、よくある問題とその解決方法を解説します。

落とし穴1: 無限ループ


useEffectが依存配列を正しく設定していない場合、無限ループが発生することがあります。

問題例

依存配列を省略すると、レンダリングのたびにuseEffectが実行されます。

useEffect(() => {
  setCount((prev) => prev + 1); // 状態更新が繰り返され無限ループに
});

解決策

適切に依存配列を指定し、副作用の実行条件を制御します。

useEffect(() => {
  setCount((prev) => prev + 1);
}, []); // 初回レンダリング時のみ実行

落とし穴2: 依存配列の不完全な設定


useEffect内で参照している変数を依存配列に含めないと、不整合な状態や予期しない動作が発生します。

問題例

以下のコードでは、userIdが変更されてもuseEffectが実行されません。

useEffect(() => {
  fetch(`https://api.example.com/user/${userId}`); // userIdが依存配列にない
}, []); // userIdの変更を検知しない

解決策

すべての依存変数を依存配列に含めます。

useEffect(() => {
  fetch(`https://api.example.com/user/${userId}`);
}, [userId]); // userIdの変更時に再実行

落とし穴3: 不適切なクリーンアップ


イベントリスナーやタイマーのクリーンアップを忘れると、メモリリークや重複処理が発生します。

問題例

以下のコードでは、イベントリスナーが適切に解除されません。

useEffect(() => {
  window.addEventListener("resize", handleResize);
}, []); // クリーンアップが未実装

解決策

クリーンアップ関数を返して、リスナーを解除します。

useEffect(() => {
  const handleResize = () => {
    console.log("Resized");
  };

  window.addEventListener("resize", handleResize);

  return () => {
    window.removeEventListener("resize", handleResize); // クリーンアップ
  };
}, []);

落とし穴4: 過剰な副作用の実行


頻繁に状態が変化する場合、useEffectが無駄に実行され、パフォーマンスが低下します。

問題例

以下のコードでは、状態が変わるたびにuseEffectが実行されます。

useEffect(() => {
  console.log("Effect executed");
}, [state1, state2]); // 状態の頻繁な変更が原因

解決策

依存配列を最適化し、必要最低限の状態を指定します。また、useMemouseCallbackで計算や関数をメモ化します。

useEffect(() => {
  console.log("Optimized effect");
}, [state1]); // 必要な依存関係のみ指定

落とし穴5: 非同期処理の競合


非同期処理がuseEffect内で競合すると、予期しない結果が返される場合があります。

問題例

以下のコードでは、userIdが変更されると、先に送信したリクエストの結果が後に返る可能性があります。

useEffect(() => {
  fetchUserData(userId).then((data) => setUser(data));
}, [userId]); // リクエストの順序が保証されない

解決策

非同期処理をキャンセルする仕組みを追加します。

useEffect(() => {
  let isMounted = true;

  fetchUserData(userId).then((data) => {
    if (isMounted) {
      setUser(data);
    }
  });

  return () => {
    isMounted = false; // クリーンアップ
  };
}, [userId]);

まとめ

  • 依存配列を正確に設定する: 使用するすべての変数や関数を指定します。
  • クリーンアップを忘れない: メモリリークや不要な処理を防ぎます。
  • 非同期処理を管理する: キャンセルロジックを追加して安全な処理を実現します。

これらの回避策を実践することで、useEffectの問題を未然に防ぎ、安定したReactアプリケーションを構築できます。

実践例:スクロールイベントのハンドリング

スクロールイベントをuseEffectでハンドリングすることで、ページスクロールに基づく動的なインタラクションを実現できます。このセクションでは、特定の位置にスクロールした際にアクションをトリガーする方法を例を交えて解説します。

基本的なスクロールイベントの設定

以下は、スクロール位置をコンソールに表示するシンプルな例です。

import React, { useEffect } from "react";

function ScrollLogger() {
  useEffect(() => {
    const handleScroll = () => {
      console.log(`Scroll position: ${window.scrollY}`);
    };

    // スクロールイベントリスナーを追加
    window.addEventListener("scroll", handleScroll);

    // クリーンアップ処理でリスナーを解除
    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, []);

  return <div style={{ height: "200vh" }}>Scroll to see the effect</div>;
}

export default ScrollLogger;

解説

  • window.scrollYを使って、現在の垂直スクロール位置を取得します。
  • 必ずクリーンアップ関数でremoveEventListenerを呼び出し、イベントリスナーを解除します。

特定のスクロール位置でアクションをトリガー

特定の位置にスクロールしたときにイベントを発生させたい場合の例です。

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

function TriggerOnScroll() {
  const [isTriggered, setIsTriggered] = useState(false);

  useEffect(() => {
    const handleScroll = () => {
      if (window.scrollY > 300 && !isTriggered) {
        setIsTriggered(true);
        console.log("Action triggered at 300px!");
      }
    };

    window.addEventListener("scroll", handleScroll);

    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, [isTriggered]); // isTriggeredを依存配列に追加

  return (
    <div style={{ height: "200vh" }}>
      {isTriggered ? <p>Action triggered!</p> : <p>Scroll down to trigger action</p>}
    </div>
  );
}

export default TriggerOnScroll;

解説

  • window.scrollY > 300 でスクロール位置を条件判定しています。
  • 状態isTriggeredを使用して、イベントが1回のみトリガーされるように制御しています。

スクロール方向の判定

スクロール方向(上か下か)を判定して、それぞれ異なるアクションを実行する例です。

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

function ScrollDirection() {
  const [scrollDirection, setScrollDirection] = useState(null);
  const [lastScrollY, setLastScrollY] = useState(0);

  useEffect(() => {
    const handleScroll = () => {
      const currentScrollY = window.scrollY;

      if (currentScrollY > lastScrollY) {
        setScrollDirection("down");
      } else if (currentScrollY < lastScrollY) {
        setScrollDirection("up");
      }

      setLastScrollY(currentScrollY);
    };

    window.addEventListener("scroll", handleScroll);

    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, [lastScrollY]);

  return (
    <div style={{ height: "200vh" }}>
      <p>Scrolling {scrollDirection}</p>
    </div>
  );
}

export default ScrollDirection;

解説

  • 現在のスクロール位置と最後のスクロール位置を比較して方向を判定します。
  • 状態scrollDirectionにスクロール方向を格納します。

パフォーマンス向上のための最適化

スクロールイベントは頻繁に発生するため、パフォーマンスが低下する可能性があります。最適化するにはデバウンスまたはスロットリングを利用します。

以下はデバウンスを使用した例です。

import React, { useEffect } from "react";
import _ from "lodash"; // lodashを使用

function OptimizedScroll() {
  useEffect(() => {
    const handleScroll = _.debounce(() => {
      console.log(`Optimized Scroll position: ${window.scrollY}`);
    }, 200); // 200ms間隔で実行

    window.addEventListener("scroll", handleScroll);

    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, []);

  return <div style={{ height: "200vh" }}>Scroll to see optimized logging</div>;
}

export default OptimizedScroll;

解説

  • デバウンス: 最後のスクロールイベントから一定時間が経過した後に処理を実行します。
  • lodash.debounceを利用して簡単に実装できます。

まとめ


スクロールイベントのハンドリングでは、次のポイントを押さえることが重要です:

  1. 必ずクリーンアップ関数でリスナーを解除する。
  2. 不要な再レンダリングを防ぐため、状態や依存配列を適切に設定する。
  3. パフォーマンス向上のためにデバウンスやスロットリングを活用する。

これらを実践することで、スクロールに基づくインタラクションを効率的に実現できます。

応用例:APIデータ取得時のイベント処理

Reactアプリケーションでは、APIからのデータ取得を特定のイベントと組み合わせることで、柔軟な動的機能を実現できます。このセクションでは、APIリクエストとイベント処理を連携させる応用例を紹介します。

API呼び出しをトリガーするイベントの設定

以下は、ボタンクリックでAPIからデータを取得し、その結果を表示する基本的な例です。

import React, { useState } from "react";

function FetchOnButtonClick() {
  const [data, setData] = useState(null);

  const handleFetch = async () => {
    try {
      const response = await fetch("https://jsonplaceholder.typicode.com/posts/1");
      const result = await response.json();
      setData(result);
    } catch (error) {
      console.error("Error fetching data:", error);
    }
  };

  return (
    <div>
      <button onClick={handleFetch}>Fetch Data</button>
      {data && (
        <div>
          <h3>{data.title}</h3>
          <p>{data.body}</p>
        </div>
      )}
    </div>
  );
}

export default FetchOnButtonClick;

解説

  • ボタンのクリックイベントをトリガーにしてAPIを呼び出します。
  • 取得したデータを状態dataに保存し、コンポーネント内で表示します。

スクロール位置に応じたAPIデータの取得

次に、スクロール位置に応じてAPIを呼び出し、新しいデータを動的にロードする例を示します。

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

function InfiniteScroll() {
  const [items, setItems] = useState([]);
  const [page, setPage] = useState(1);

  useEffect(() => {
    const fetchItems = async () => {
      try {
        const response = await fetch(`https://jsonplaceholder.typicode.com/posts?_page=${page}&_limit=5`);
        const data = await response.json();
        setItems((prevItems) => [...prevItems, ...data]);
      } catch (error) {
        console.error("Error fetching items:", error);
      }
    };

    fetchItems();
  }, [page]);

  useEffect(() => {
    const handleScroll = () => {
      if (window.innerHeight + window.scrollY >= document.body.offsetHeight) {
        setPage((prevPage) => prevPage + 1);
      }
    };

    window.addEventListener("scroll", handleScroll);

    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, []);

  return (
    <div>
      <ul>
        {items.map((item) => (
          <li key={item.id}>{item.title}</li>
        ))}
      </ul>
    </div>
  );
}

export default InfiniteScroll;

解説

  • スクロール位置がページの末尾に到達すると次のページのデータを取得します。
  • useEffectを使って、ページ番号pageの変更に応じてAPIを呼び出します。

フォーム入力とAPI呼び出しの組み合わせ

以下は、入力フィールドに文字を入力するたびにAPIを呼び出して関連データを取得する例です。

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

function SearchAPI() {
  const [query, setQuery] = useState("");
  const [results, setResults] = useState([]);

  useEffect(() => {
    const fetchResults = async () => {
      if (query.length > 2) {
        try {
          const response = await fetch(`https://jsonplaceholder.typicode.com/posts?q=${query}`);
          const data = await response.json();
          setResults(data);
        } catch (error) {
          console.error("Error fetching search results:", error);
        }
      }
    };

    fetchResults();
  }, [query]);

  return (
    <div>
      <input
        type="text"
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="Search..."
      />
      <ul>
        {results.map((result) => (
          <li key={result.id}>{result.title}</li>
        ))}
      </ul>
    </div>
  );
}

export default SearchAPI;

解説

  • 入力文字列queryが変更されるたびにAPIを呼び出します。
  • クエリ文字列の長さが一定以上の場合のみAPIを実行することで、不要なリクエストを削減します。

APIとカスタムイベントの連携

カスタムイベントを利用してAPIをトリガーする例です。

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

function CustomEventAPI() {
  const [data, setData] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch("https://jsonplaceholder.typicode.com/posts/1");
        const result = await response.json();
        setData(result);
      } catch (error) {
        console.error("Error fetching data:", error);
      }
    };

    const handleCustomEvent = () => {
      fetchData();
    };

    window.addEventListener("fetchDataEvent", handleCustomEvent);

    return () => {
      window.removeEventListener("fetchDataEvent", handleCustomEvent);
    };
  }, []);

  const triggerEvent = () => {
    const event = new Event("fetchDataEvent");
    window.dispatchEvent(event);
  };

  return (
    <div>
      <button onClick={triggerEvent}>Trigger Custom Event</button>
      {data && (
        <div>
          <h3>{data.title}</h3>
          <p>{data.body}</p>
        </div>
      )}
    </div>
  );
}

export default CustomEventAPI;

解説

  • カスタムイベントfetchDataEventをトリガーしてAPI呼び出しを行います。
  • 他のコンポーネントやモジュールから簡単にAPIトリガーを実現できます。

まとめ

  • API呼び出しをユーザーイベントやスクロール、カスタムイベントと組み合わせることで、より動的なアプリケーションが構築可能です。
  • イベント処理とAPIロジックを分離し、コードの可読性と再利用性を向上させることが重要です。

これらの応用例を組み合わせることで、柔軟でインタラクティブな機能を実現できます。

まとめ

本記事では、ReactのuseEffectを活用して特定のイベント発生時にアクションをトリガーする方法を詳しく解説しました。useEffectの基本概念から、イベントリスナーの設定、クリーンアップ、依存配列の使い方、さらにスクロールイベントやAPI呼び出しの応用例まで、幅広く取り上げました。

useEffectを正しく使用することで、Reactアプリケーションのパフォーマンスと保守性を向上させることができます。特に、依存配列の設定やクリーンアップ処理を徹底することで、予期しない動作やメモリリークを防止できます。

この知識を活用し、柔軟で拡張性の高いイベント駆動型のReactアプリケーションを構築してください。

コメント

コメントする

目次
  1. useEffectの基本概念
    1. 主な特徴
    2. 基本構文
    3. 使用例
    4. 適用範囲
  2. useEffectでイベントリスナーを設定する方法
    1. 基本構文
    2. DOMイベントの設定例
    3. 依存配列の活用
    4. 注意点
  3. クリーンアップの重要性と方法
    1. クリーンアップが必要な理由
    2. 基本構文
    3. 具体例
    4. 依存配列とクリーンアップ
    5. 注意点
  4. 依存配列の使い方と注意点
    1. 依存配列とは
    2. 依存配列の使い方
    3. 依存配列の注意点
    4. 依存配列を省略するリスク
    5. まとめ
  5. カスタムイベントのトリガー方法
    1. カスタムイベントの基本概念
    2. カスタムイベントの実装例
    3. カスタムイベントを使用したコンポーネント間通信
    4. カスタムイベントの応用例
    5. 注意点
  6. よくある落とし穴とその回避策
    1. 落とし穴1: 無限ループ
    2. 落とし穴2: 依存配列の不完全な設定
    3. 落とし穴3: 不適切なクリーンアップ
    4. 落とし穴4: 過剰な副作用の実行
    5. 落とし穴5: 非同期処理の競合
    6. まとめ
  7. 実践例:スクロールイベントのハンドリング
    1. 基本的なスクロールイベントの設定
    2. 特定のスクロール位置でアクションをトリガー
    3. スクロール方向の判定
    4. パフォーマンス向上のための最適化
    5. まとめ
  8. 応用例:APIデータ取得時のイベント処理
    1. API呼び出しをトリガーするイベントの設定
    2. スクロール位置に応じたAPIデータの取得
    3. フォーム入力とAPI呼び出しの組み合わせ
    4. APIとカスタムイベントの連携
    5. まとめ
  9. まとめ