「APIからデータを取得したいけど、どこに書けばいいの?」 「画面が表示されるたびに処理を実行したいんだけど…」
Reactで開発していると、「コンポーネントの表示」以外のことをしたくなる場面が出てきますよね。そんなときに使うのがuseEffect
です。
今回は、React Hooksの重要な機能「useEffect」の基礎について、実践的なコード例を交えながら解説していきます。この記事を読めば、データの取得やイベントの購読など、副作用を適切に処理できるようになり、より本格的なアプリケーション開発ができるようになるはずです。
useEffectの基礎:副作用の処理を理解しよう
「APIからデータを取得したいけど、どこに書けばいいの?」 「画面が表示されるたびに処理を実行したいんだけど…」
Reactで開発していると、「コンポーネントの表示」以外のことをしたくなる場面が出てきますよね。そんなときに使うのがuseEffect
です。
今回は、React Hooksの重要な機能「useEffect」の基礎について、実践的なコード例を交えながら解説していきます。この記事を読めば、データの取得やイベントの購読など、副作用を適切に処理できるようになり、より本格的なアプリケーション開発ができるようになるはずです。
useEffectとは何か
useEffectは、コンポーネントの「副作用」を処理するためのHookです。副作用とは、レンダリング以外の処理のことで、例えば:
- APIからのデータ取得
- イベントリスナーの設定
- タイマーの設定
- ローカルストレージの操作
などが該当します。
useEffectの基本的な使い方
import { useEffect } from 'react';
function ExampleComponent() {
useEffect(() => {
// 実行したい副作用の処理
console.log('コンポーネントがマウントされました');
// クリーンアップ関数(必要な場合)
return () => {
console.log('コンポーネントがアンマウントされます');
};
}, []); // 依存配列
return <div>Example</div>;
}
実践的な使用例
1. データ取得の実装
import React, { useState, useEffect } from 'react';
const DataFetching = () => {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchPosts = async () => {
try {
// 実際のAPIエンドポイントに置き換えてください
const response = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=5');
if (!response.ok) throw new Error('データの取得に失敗しました');
const data = await response.json();
setPosts(data);
setLoading(false);
} catch (err) {
setError(err.message);
setLoading(false);
}
};
fetchPosts();
// クリーンアップ関数
return () => {
// コンポーネントのアンマウント時やre-fetch時にキャンセルしたい処理があればここに書く
};
}, []); // 空の依存配列 = マウント時のみ実行
if (loading) {
return (
<div className="p-4">
<div className="animate-pulse space-y-4">
{[1, 2, 3].map((n) => (
<div key={n} className="h-24 bg-gray-200 rounded"></div>
))}
</div>
</div>
);
}
if (error) {
return (
<div className="p-4">
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
{error}
</div>
</div>
);
}
return (
<div className="p-4 space-y-4">
{posts.map(post => (
<div key={post.id} className="border rounded p-4 hover:shadow-md transition-shadow">
<h2 className="text-xl font-bold mb-2">{post.title}</h2>
<p className="text-gray-600">{post.body}</p>
</div>
))}
</div>
);
};
export default DataFetching;
2. ウィンドウサイズの監視
import React, { useState, useEffect } from 'react';
const WindowSizeTracker = () => {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight
});
const [breakpoint, setBreakpoint] = useState('');
useEffect(() => {
// ウィンドウサイズの変更を監視する関数
const handleResize = () => {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight
});
// ブレークポイントの設定
if (window.innerWidth < 640) {
setBreakpoint('sm');
} else if (window.innerWidth < 768) {
setBreakpoint('md');
} else if (window.innerWidth < 1024) {
setBreakpoint('lg');
} else {
setBreakpoint('xl');
}
};
// イベントリスナーを登録
window.addEventListener('resize', handleResize);
// クリーンアップ関数
return () => {
// イベントリスナーを解除
window.removeEventListener('resize', handleResize);
};
}, []); // 空の依存配列 = マウント時のみ実行
return (
<div className="p-4 border rounded shadow-sm">
<h2 className="text-xl font-bold mb-4">ウィンドウサイズ</h2>
<div className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div className="p-3 bg-blue-100 rounded">
<p className="font-medium">幅</p>
<p className="text-xl">{windowSize.width}px</p>
</div>
<div className="p-3 bg-green-100 rounded">
<p className="font-medium">高さ</p>
<p className="text-xl">{windowSize.height}px</p>
</div>
</div>
<div className="p-3 bg-purple-100 rounded">
<p className="font-medium">現在のブレークポイント</p>
<p className="text-xl">{breakpoint}</p>
</div>
</div>
<div className="mt-4 text-sm text-gray-600">
<p>ブラウザのウィンドウサイズを変更してみてください。</p>
<p>値がリアルタイムで更新されます。</p>
</div>
</div>
);
};
export default WindowSizeTracker;
依存配列の理解
useEffectの依存配列は、副作用の実行タイミングを制御する重要な要素です。
1. 空の依存配列 []
useEffect(() => {
console.log('マウント時のみ実行');
}, []);
- コンポーネントがマウントされた時のみ実行
- データの初期取得などに使用
2. 依存配列なし
useEffect(() => {
console.log('毎回のレンダリングで実行');
});
- レンダリングのたびに実行
- パフォーマンスに注意が必要
3. 値を指定した依存配列
useEffect(() => {
console.log('countが変更された時に実行');
}, [count]);
- 指定した値が変更された時のみ実行
- 必要な依存関係は必ず含める
クリーンアップの重要性
クリーンアップ関数は、コンポーネントのアンマウント時やre-render前に実行される重要な機能です。
イベントリスナーのクリーンアップ
useEffect(() => {
const handler = () => {
console.log('スクロール');
};
window.addEventListener('scroll', handler);
// クリーンアップ関数
return () => {
window.removeEventListener('scroll', handler);
};
}, []);
タイマーのクリーンアップ
useEffect(() => {
const timer = setInterval(() => {
console.log('1秒経過');
}, 1000);
// クリーンアップ関数
return () => {
clearInterval(timer);
};
}, []);
よくある間違いと解決策
1. 依存配列の指定ミス
// ❌ 依存配列が不足している
useEffect(() => {
setTotal(count * price);
}, [count]); // priceも依存配列に含めるべき
// ✅ 正しい依存配列の指定
useEffect(() => {
setTotal(count * price);
}, [count, price]);
2. 非同期関数を直接渡す
// ❌ 非同期関数を直接効果として指定
useEffect(async () => {
const data = await fetchData();
}, []);
// ✅ 内部で非同期関数を定義
useEffect(() => {
const fetchData = async () => {
const data = await fetch();
};
fetchData();
}, []);
3. 無限ループの発生
// ❌ 無限ループが発生する
useEffect(() => {
setCount(count + 1);
}, [count]);
// ✅ 必要な場合のみ更新
useEffect(() => {
if (shouldUpdate) {
setCount(count + 1);
}
}, [count, shouldUpdate]);
まとめ
useEffectについて学んだ内容を整理しましょう:
- useEffectは副作用を処理するためのHook
- 依存配列で実行タイミングを制御できる
- クリーンアップ関数でリソースの解放が必要
- 適切な依存関係の管理が重要
- 非同期処理は内部で定義する
次回は「APIとの連携」について学んでいきます。今回学んだuseEffectを使って、実際のAPIとの通信処理を実装する方法を解説していきます。