はじめに
こんにちは、株式会社TOKOSのツキヤです!
今回は、ReactのuseEffect
を使う時に重要になるcleanup(クリーンアップ)について、簡単な説明とやってしまいそうなミスへの対策方法を書きます!
自分も1度ハマったので、誰かの参考になれば幸いです🙌
対象読者
- Reactのcleanupについて知りたい方
- 自分のcleanupが正しくできているか気になっている方
cleanup(クリーンアップ)とは?
まずはざっくりとcleanupについて説明します!
cleanupとは簡単に言うと「useEffect
の中でreturn
する関数」のことです!
例えば下記コードのような形です
import React, { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
// 副作用の処理(例:イベントリスナーの設定)
const handleResize = () => {
console.log('ウィンドウがリサイズされました!!');
};
window.addEventListener('resize', handleResize);
// ここがクリーンアップ(関数)
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return <div>ウィンドウがリサイズされるとcosole.logが出力されるよ</div>;
}
上記のuseEffect内のreturnの部分です!useEffect
内で宣言したイベントリスナーをreturn
内でremove
しています。
これにより、このMyComponent
というコンポーネントがアンマウントされる時に一緒にイベントリスナーも消してくれるという感じです✨
逆に言うと、このcleanupが無い場合は、このコンポーネントがマウントされる度に同じイベントリスナーがどんどん追加されていき、重くなったり意図しない挙動になったりします🥲
今回の例で言うと、1回のリサイズでconsole.log
が何回も出てしまう感じだね!
なので、基本的にはuseEffect
を使う時はcleanupをする必要があるかは常に検討すべきです!
以下の記事が参考になります、詳しく知りたい方は見てみてください。
cleanup(クリーンアップ)ができていない実装
上記の例は、cleanupができている実装です!
上記のコードを再掲します。
import React, { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
// 副作用の処理(例:イベントリスナーの設定)
const handleResize = () => {
console.log('ウィンドウがリサイズされました!!');
};
window.addEventListener('resize', handleResize);
// ここがクリーンアップ(関数)
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return <div>ウィンドウがリサイズされるとcosole.logが出力されるよ</div>;
}
それでは、cleanupができていない実装例はこちらです!
import React, { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
window.addEventListener('resize', () => {
console.log('ウィンドウがリサイズされました!!');
});
// ここがクリーンアップ(関数)
return () => {
window.removeEventListener('resize', () => {
console.log('ウィンドウがリサイズされました!!');
});
};
}, []);
return <div>ウィンドウがリサイズされるとcosole.logが出力されるよ</div>;
}
handleResize
として宣言していた部分をaddEventListener
とremoveEventListener
の第二引数内に直接アロー関数として全く同じものをそれぞれ記述した形になります。
一見同じ挙動になりそうですね🤔
なぜできていないのか
これについては、関数は同じ記述をしたとしても、違うものとしてみなされるためです!
分かりやすい例をコードとして記載します!
const foo = 1;
const bar = 1;
console.log(foo === bar) // => true が出力される✅
const hoge = () => { console.log('ウィンドウがリサイズされました!!') };
const huga = () => { console.log('ウィンドウがリサイズされました!!') };
console.log(hoge === huga) // => false が出力される⛔️
上記のような結果になるのです🧐
1や”文字列” のようなプリミティブな値は変数に格納しても等価演算子(==)でその値同士が同じであればtrueになります。
一方、関数やオブジェクト(ex. { name: “ツキヤ” }) は全く同じ値を変数に格納しても、等価演算子で比較するとfalseになります!
これを説明するためには、JavaScriptのやや踏み込んだ理解が必要になるので割愛します🙏
詳しくはこちらの書籍等を参考にしてみてください。
useEffect
の話に戻ると、addEventListener
とremoveEventListener
のそれぞれで全く同じ関数を記述したとしても、先程の例から分かるように同じ関数としてみなされないです。
なので、全く違う関数をremoveEventListener
しようとしていることになり、うまくremoveEventListener
ができないのです!!
そこで、初回の例のようにuseEffect内であらかじめ変数に関数を格納しておき、その変数をaddEventListener
とremoveEventListener
のそれぞれの第二引数に渡すことで意図通りのcleanupとなります✨
終わりに
今回は、cleanupの簡単な説明と正しいcleanupの方法についての解説をしました!
Next.js等のフレームワークを上手くを使うためにも、React自体の機能をしっかり理解しておきたいですね😎