⚛️

ref.currentをクリーンアップ関数に入れない

に公開

モーダルのようなコンポーネントを書いていて、背景をクリックしたときにモーダルをリセットする処理のために以下のコードを書いたところESLintエラーが出ました。

useEffect(() => {
    if (memoBackgroundRef.current !== null) {
      memoBackgroundRef.current.addEventListener("click", resetMemo)
    }

    return () => {
      if (memoBackgroundRef.current !== null) {
        memoBackgroundRef.current.removeEventListener("click", resetMemo)
      }
    }
  }, [resetMemo])

出現したエラー

これはuseEffectのライフサイクルに適合しない可能性があることが原因でした。
useRefで得られる値は、基本的にコンポーネントの描画中参照が変化しないため、依存配列で変更を検出することが出来ません。
変更が検出されないということは、Effect内でマウント時に実行した処理をアンマウント時にクリーンアップ関数で解除するというライフサイクルが正しく機能しなくなる可能性があります。

なので以下のように書き直す必要があります。

  useEffect(() => {
    const currentRef = memoBackgroundRef.current
    if (currentRef !== null) {
      currentRef.addEventListener("click", resetMemo)
    }

    return () => {
      if (currentRef !== null) {
        currentRef.removeEventListener("click", resetMemo)
      }
    }
  }, [resetMemo])

ローカル変数に、マウント時のref.currentの値を保持しておきます。
これにより、アンマウント時にクリーンアップ関数内のref.currentの内容がマウント時のref.currentと一致します。

Discussion