😄

useEffectの使い方について

に公開

useEffectとは

Reactの副作用フック。コンポーネントのライフサイクルに応じて処理を実行する。

基本構文

useEffect(() => {
  // 実行したい処理
}, [依存配列]);

実行タイミングのパターン

1. 毎回実行(依存配列なし)

useEffect(() => {
  console.log('レンダリング毎に実行');
  // APIコール、DOM操作など
});
  • 実行タイミング: 毎回レンダリング後
  • 用途: あまり使わない(パフォーマンス問題)

2. マウント時のみ実行(空の依存配列)

useEffect(() => {
  console.log('マウント時のみ実行');
  // 初期化処理、イベントリスナー登録など
}, []); // 空配列
  • 実行タイミング: コンポーネントマウント時のみ
  • 用途: 初期化処理、一度だけ実行したい処理

3. 依存値変更時に実行(依存配列あり)

useEffect(() => {
  console.log('依存値変更時に実行');
  // データ取得、状態更新など
}, [count, name]); // 依存配列
  • 実行タイミング: 依存配列の値が変更された時
  • 用途: 特定の値の変更に応じた処理

クリーンアップ関数

基本的なクリーンアップ

useEffect(() => {
  const timer = setInterval(() => {
    console.log('タイマー実行');
  }, 1000);

  // クリーンアップ関数
  return () => {
    clearInterval(timer);
  };
}, []);

イベントリスナーのクリーンアップ

useEffect(() => {
  const handleResize = () => {
    console.log('リサイズ');
  };

  window.addEventListener('resize', handleResize);

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

実際の使用例

データ取得パターン

// あなたのコードの例
useEffect(() => {
  const fetchData = async () => {
    if (!isOpen || !coordinateId) return;

    setIsLoading(true);
    try {
      const response = await getCoordinateDetail(token, coordinateId, userName);
      if (response.data) {
        setCoordinateData(response.data);
      }
    } catch (error) {
      console.error('Error:', error);
    } finally {
      setIsLoading(false);
    }
  };

  fetchData();
}, [isOpen, coordinateId, token, userName]);

モーダル開閉パターン

useEffect(() => {
  if (isOpen) {
    // モーダルが開いた時の処理
    document.body.style.overflow = 'hidden';
  } else {
    // モーダルが閉じた時の処理
    document.body.style.overflow = 'auto';
  }

  return () => {
    // クリーンアップ
    document.body.style.overflow = 'auto';
  };
}, [isOpen]);

よくある間違いと対処法

1. 無限ループ

// ❌ 間違い - 無限ループ
const [data, setData] = useState(null);

useEffect(() => {
  setData(someValue); // dataが変更される
}, [data]); // dataが変更されるたびに実行される

// ✅ 正しい
useEffect(() => {
  setData(someValue);
}, []); // 初回のみ実行

2. 依存配列の不備

// ❌ 間違い - 依存配列が不完全
useEffect(() => {
  fetchData(userId);
}, []); // userIdが変更されても実行されない

// ✅ 正しい
useEffect(() => {
  fetchData(userId);
}, [userId]); // userIdが変更されたら実行される

3. 古いstateの参照

// ❌ 間違い - 古いcountを参照
useEffect(() => {
  const timer = setInterval(() => {
    setCount(count + 1); // 古いcountを参照
  }, 1000);

  return () => clearInterval(timer);
}, []); // countが依存配列にない

// ✅ 正しい
useEffect(() => {
  const timer = setInterval(() => {
    setCount(prevCount => prevCount + 1); // 関数型更新
  }, 1000);

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

パフォーマンス最適化

1. 依存配列の最適化

// ❌ 重い処理が毎回実行される
useEffect(() => {
  heavyCalculation();
}, [user]); // userオブジェクト全体が変更されるたびに実行

// ✅ 必要な値のみ依存
useEffect(() => {
  heavyCalculation();
}, [user.id]); // user.idが変更された時のみ実行

2. コールバック関数の最適化

// useCallbackと組み合わせ
const fetchData = useCallback(async () => {
  const response = await api.getData();
  setData(response);
}, []);

useEffect(() => {
  fetchData();
}, [fetchData]);

まとめ

依存配列 実行タイミング 用途
なし 毎回レンダリング後 避けるべき
[] マウント時のみ 初期化処理
[値] 値変更時 値に応じた処理

重要ポイント:

  • 依存配列には使用する全ての値を含める
  • クリーンアップ関数でリソースを適切に解放
  • 無限ループに注意
  • パフォーマンスを考慮した依存配列の設計

Discussion