Reactのfunctionコードでスクロールイベント等を実装すると、
イベント関数をuseCallbackでくくってメモ化しておかないとremoveEventListenerが働かないとか、
スクロールで使用するフラグはuseRefで再レンダリングされないようにする...など、
意外と気に掛ける点が多かったので、備忘録も込めてコードを載せておきます。

import React, {
  useState, useEffect, useRef, useCallback,
} from 'react'

const TestDom = () => {
  const [isDisplay, setIsDisplay] = useState(false)

  const isRunning = useRef(false) // スクロール多発防止用フラグ

  // リスナに登録する関数
  const isScrollToggle = useCallback(() => {
    if (isRunning.current) return
    isRunning.current = true
    const scrollTop = window.pageYOffset || document.documentElement.scrollTop
    requestAnimationFrame(() => {
      if (scrollTop > 100) {
        setIsDisplay(true)
      } else {
        setIsDisplay(false)
      }
      isRunning.current = false
    })
  }, [])

  // 登録と後始末
  useEffect(() => {
    document.addEventListener('scroll', isScrollToggle, { passive: true })
    return () => {
      document.removeEventListener('scroll', isScrollToggle, { passive: true })
    }
  }, [])

 // バツボタンでリスナ削除~ などはこのように
  const onClickClose = () => {
    document.removeEventListener('scroll', isScrollToggle, { passive: true })
    setIsDisplay(false)
  }