useEffect クリーンアップ徹底解説
ReactのuseEffect
フックは、コンポーネントの副作用を管理するための強力なツールです。しかし、不適切な使用により、メモリリークや不必要な副作用が発生する可能性があります。これを防ぐために、useEffect
のクリーンアップ関数を使用して、コンポーネントがアンマウントされる前に、購読や非同期タスクを適切にキャンセルする必要があります。
なぜクリーンアップが必要なのか?
- 副作用の制御
アンマウントされたコンポーネントに対して、不要な副作用が発生することを防ぎます。 - メモリリークの防止
コンポーネントがアンマウントされた後も、購読や非同期タスクが実行され続けると、メモリが無駄に使用されます。
クリーンアップ関数の使い方
useEffect
フックの第2引数に、クリーンアップ関数を受け取ります。この関数は、コンポーネントがアンマウントされる直前に実行されます。
useEffect(() => {
// 購読や非同期タスクの開始
const subscription = someSubscription();
const intervalId = setInterval(() => {
// 定期的な処理
}, 1000);
// クリーンアップ関数
return () => {
// 購読の解除
subscription.unsubscribe();
// インターバルのクリア
clearInterval(intervalId);
};
}, [dependencyArray]);
具体的な例
-
WebSocketの接続
useEffect(() => { const socket = new WebSocket('ws://example.com'); socket.onmessage = (event) => { // メッセージ受信時の処理 }; return () => { socket.close(); }; }, [dependencyArray]);
-
外部APIへのリクエスト
useEffect(() => { const fetchData = async () => { const response = await fetch('https://api.example.com/data'); // ... }; fetchData(); return () => { // 非同期リクエストをキャンセルする方法はAPIによって異なる // 例えば、AbortControllerを使用する場合: controller.abort(); }; }, [dependencyArray]);
重要なポイント
- クリーンアップ関数は、可能な限り簡潔かつ効率的に実装しましょう。
- クリーンアップ関数内でエラーが発生した場合、コンポーネントのアンマウントが妨げられる可能性があります。
- クリーンアップ関数は、
useEffect
の依存配列が変更された場合にも実行されます。
useEffect(() => {
// 購読や非同期タスクの開始
const subscription = someSubscription();
const intervalId = setInterval(() => {
// 定期的な処理
}, 1000);
// クリーンアップ関数
return () => {
// 購読の解除
subscription.unsubscribe();
// インターバルのクリア
clearInterval(intervalId);
};
}, [dependencyArray]);
ReactのuseEffect
フックのクリーンアップ関数は、コンポーネントがアンマウントされる前に、購読や非同期タスクをキャンセルするための重要なツールです。しかし、特定の状況では、クリーンアップ関数をより効果的に使用するための代替方法があります。
代替方法
-
useRefフックによる参照の管理
useRef
フックを使用して、購読や非同期タスクの参照を保持します。- クリーンアップ関数内で、これらの参照を使用して、適切なキャンセル処理を行います。
import { useRef, useEffect } from 'react'; function MyComponent() { const subscriptionRef = useRef(null); useEffect(() => { const subscription = someSubscription(); subscriptionRef.current = subscription; return () => { if (subscriptionRef.current) { subscriptionRef.current.unsubscribe(); } }; }, []); // ... }
-
カスタムフックによる抽象化
- 複雑なクリーンアップロジックをカスタムフックにカプセル化します。
- 他のコンポーネントからこのカスタムフックを再利用できます。
import { useEffect, useRef } from 'react'; function useSubscription(subscriptionCallback) { const subscriptionRef = useRef(null); useEffect(() => { const subscription = subscriptionCallback(); subscriptionRef.current = subscription; return () => { if (subscriptionRef.current) { subscriptionRef.current.unsubscribe(); } }; }, [subscriptionCallback]); } function MyComponent() { useSubscription(() => someSubscription()); // ... }
-
useCallbackフックによるメモ化
useCallback
フックを使用して、クリーンアップ関数をメモ化し、不必要な再レンダリングを防ぎます。
import { useEffect, useCallback } from 'react'; function MyComponent() { const cleanup = useCallback(() => { // クリーンアップロジック }, []); useEffect(() => { // ... return cleanup; }, []); // ... }
選択のポイント
- パフォーマンス
useCallback
フックは、クリーンアップ関数が頻繁に再レンダリングされる場合にパフォーマンスを向上させることができます。 - 再利用性
カスタムフックは、複雑なクリーンアップロジックを再利用したい場合に便利です。 - シンプルさ
useRef
フックは、基本的なクリーンアップロジックに適しています。
reactjs