useEffect — 副作用とライフサイクル
このトピックを終えると
useEffectがいつ実行されるか、依存配列とは何か、クリーンアップ関数がなぜ必要なのかを理解できるようになります。
副作用とは
Reactコンポーネントの主な役割は、状態を受け取ってUIを描画することです。しかし、それ以外の作業も必要です。
- APIからデータを取得する
- タイマーを設定する
- ドキュメントのタイトルを変更する
- イベントリスナーを登録する
このような作業を**副作用(side effect)**と呼びます。useEffectはこの副作用を処理するHookです。
基本的な使い方
javascript
import { useEffect, useState } from "react";
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [userId]);
if (!user) return <p>Loading...</p>;
return <h1>{user.name}</h1>;
}useEffectは2つの引数を受け取ります。
- 実行する関数(エフェクト)
- 依存配列(いつ再度実行するか)
依存配列
依存配列によって実行のタイミングが変わります。
javascript
// すべてのレンダリングで実行(依存配列なし)
useEffect(() => {
console.log("毎回実行");
});
// マウント時に一度だけ実行(空の配列)
useEffect(() => {
console.log("最初に一度だけ");
}, []);
// userIdが変更されるたびに実行
useEffect(() => {
fetchUser(userId);
}, [userId]);Reactは、依存配列の値が以前のレンダリングと異なるかどうかを比較します。異なれば、エフェクトを再度実行します。
クリーンアップ関数
javascript
useEffect(() => {
const timer = setInterval(() => {
console.log("tick");
}, 1000);
return () => {
clearInterval(timer);
};
}, []);エフェクト関数で関数を返すと、それがクリーンアップ関数になります。次の状況で実行されます。
- コンポーネントがアンマウントされるとき(画面から消えるとき)
- エフェクトが再度実行される直前(依存関係が変更されたとき)
タイマーの解除、イベントリスナーの削除、購読の取り消しなどの整理作業に使用します。
javascript
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);よくある間違い:無限ループ
javascript
// 無限ループ!
useEffect(() => {
setCount(count + 1);
}); // 依存配列なし → すべてのレンダリングで実行 → setState → リレンダリング → 再び実行エフェクト内で状態を変更すると、リレンダリングが発生し、依存配列がない場合、エフェクトが再び実行されます。
javascript
// 正しい方法
useEffect(() => {
fetchData().then(setData);
}, []); // 空の配列 → 一度だけ実行重要なポイント
useEffectはレンダリング後に副作用を実行します。 依存配列が空の配列の場合、マウント時に一度、値がある場合は、その値が変更されたときに実行されます。 クリーンアップ関数でタイマー、リスナーなどを整理して、メモリリークを防ぐ必要があります。