😄
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