この記事を作った動機

 React で addEventListener を使ったら、再描画される事に、addEventListener されまくって、例えばマウスボタンが押されたというイベントが発火したときに、無数の同じ処理が走りまくるという事態になった。

 これは以下のように、react の要素の一部として書いたときには、起こらなかった。

export function OverlayWindow({ children, arg }:{ children:ReactNode, arg:OverlayWindowArgs }){
    // 何かしらのコード

    if(visible){
        return (<div className={OverlayWindowContaierClassName} style={windowPosStyle}>
            <div className="windowHeader move bg-yellow-600 w-full h-[2rem] justify-center place-items-center align-middle text-center"
                onMouseDown={windowHandlers.mousedown}   // addEventListener 相当
                onTouchStart={windowHandlers.touchstart} // addEventListener 相当
                >
                    
                <div className="title h-[1rem] absolute text-white">{arg.title}</div>
                <div className="close size-[2rem] bg-red-700 ml-auto" onClick={() => {setVisible(false)}}></div>
            </div>
            <div className="content bg-gray-900 min-h-[5rem] w-full flex justify-center place-items-center align-middle text-center
 items-center">
                {children}
            </div>

        </div>)
    }
}

 それでとりあえず、解決状態だと思われる、アプリ自体の動作を重くしないレベルの実装にもっていくまでについて、簡易的に記録を取ろうと思い、この記事を作った。

環境

  • Vite
  • React
  • TypeScript
  • Tailwind CSS
  • zustand

問題の状態

 このようにして、グローバルにすべての mousemove などのイベントの発火を要素関係なく受け取ろうとすると、再描画時に何度も addEventListener で特定のイベントに対し、コールバック関数が登録されてしまう。

export function OverlayWindow({ children, arg }:{ children:ReactNode, arg:OverlayWindowArgs }){
    
    // 何かしらのコード

    addEventListener("touchend",windowHandlers.touchend)
    addEventListener("mouseup",windowHandlers.mouseup)
    addEventListener("touchmove",windowHandlers.touchmove)
    addEventListener("mousemove",windowHandlers.mousemove)

    // 何かしらのコード
}

私が今回取った対応

 useRef を使って、コンポーネントが初期化されたかどうかに関する状態を管理することにした。そうすることで一回だけ addEventListener で特定のイベントにコールバック関数を設定するようにした。

export function OverlayWindow({ children, arg }:{ children:ReactNode, arg:OverlayWindowArgs }){
    
    // 何かしらのコード

    let init = useRef(true)

    // 何かしらのコード

    if(init.current){
        // console.log("Overlay window init")
        addEventListener("touchend",windowHandlers.touchend)
        addEventListener("mouseup",windowHandlers.mouseup)

        addEventListener("touchmove",windowHandlers.touchmove)
        addEventListener("mousemove",windowHandlers.mousemove)

        init.current = false
        console.log(init.current)
    }

    // 何かしらのコード
}

参考にしたサイトとか