
【React】そのUseEffect, 本当にcleanup(クリーンアップ)できてますか?
はじめに
こんにちは、株式会社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>ウィンドウがリサイズされるとconsole.logが出力されるよ</div>
}上記のuseEffect内のreturnの部分です!
useEffect内で宣言したイベントリスナーをreturn内でremoveしています。
これにより、このMyComponentというコンポーネントがアンマウントされる時に一緒にイベントリスナーも消してくれるという感じです✨
逆に言うと、このcleanupが無い場合は、このコンポーネントがマウントされる度に同じイベントリスナーがどんどん追加されていき、重くなったり意図しない挙動になったりします🥲
今回の例で言うと、1回のリサイズでconsole.logが何回も出てしまう感じだね!
なので、基本的にはuseEffectを使う時はcleanupをする必要があるかは常に検討すべきです!
以下の記事が参考になります、詳しく知りたい方は見てみてください。
過激派が教える! useEffectの正しい使い方
zenn.devcleanup(クリーンアップ)ができていない実装
上記の例は、cleanupができている実装です!
上記のコードを再掲します。
import React, { useEffect } from "react"
function MyComponent() {
useEffect(() => {
// 副作用の処理(例:イベントリスナーの設定)
const handleResize = () => {
console.log("ウィンドウがリサイズされました!!")
}
window.addEventListener("resize", handleResize)
// ここがクリーンアップ(関数)
return () => {
window.removeEventListener("resize", handleResize)
}
}, [])
return <div>ウィンドウがリサイズされるとconsole.logが出力されるよ</div>
}それでは、cleanupができていない実装例はこちらです!
import React, { useEffect } from "react"
function MyComponent() {
useEffect(() => {
window.addEventListener("resize", () => {
console.log("ウィンドウがリサイズされました!!")
})
// ここがクリーンアップ(関数)
return () => {
window.removeEventListener("resize", () => {
console.log("ウィンドウがリサイズされました!!")
})
}
}, [])
return <div>ウィンドウがリサイズされるとconsole.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自体の機能をしっかり理解しておきたいですね😎




