React useEffectの後片付けでイベント登録を解除する
画面の幅が変わったときに表示を更新したい場合、ブラウザのresizeイベントを登録できます。登録したままにすると、コンポーネントが不要になったあとも処理が残るため、useEffectの後片付けを用意します。
登録と解除を対にする
import { useEffect, useState } from 'react';
export const WindowWidth = () => {
const [width, setWidth] = useState(() => window.innerWidth);
useEffect(() => {
const update = () => setWidth(window.innerWidth);
window.addEventListener('resize', update);
return () => window.removeEventListener('resize', update);
}, []);
return <p>幅: {width}px</p>;
};返した関数がcleanupです。コンポーネントが外れるときや、依存値が変わってEffectを再実行する前に呼ばれます。
cleanupが必要な処理を見分ける
イベント購読、タイマー、WebSocket接続など、コンポーネントの外側に残る処理には対応する解除が必要です。例えばタイマーなら以下のようにします。
useEffect(() => {
const timer = window.setInterval(() => console.log('更新確認'), 1000);
return () => window.clearInterval(timer);
}, []);開発環境のStrict Modeでは、Effectのセットアップとcleanupが追加で実行されることがあります。そこで同じイベントが二重に動くなら、解除の不足を疑ってください。本番での実行回数を合わせるためにStrict Modeを外す必要はありません。
なぜ同じ関数を渡すか
removeEventListenerには、登録時と同じ関数を渡します。別の無名関数を作って渡しても解除できません。
この例はブラウザでのみ描画する画面を想定しています。SSRを使う場合は、初期値を固定し、ブラウザ上で動くEffect内で幅を取得する設計が必要です。