You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React函数组件添加window事件监听器的正确方法及常见疑问

问题解答

1. 频繁增删window监听器是否属于常规实践?

不属于,这是可以避免的不必要操作。
虽然单个addEventListener/removeEventListener的执行开销极低,少量触发不会有感知级别的性能问题,但如果每次组件渲染都执行,属于典型的可优化冗余操作,行业内不会把这种写法当作标准实践。

你可以通过两种Hooks写法规避这个问题,完全不需要切换到类组件:

方案1:用useCallback缓存事件回调

仅当回调依赖的props/state变化时才更新回调实例,大幅降低useEffect的触发频率:

const Component = ({ someProp }) => {
  // 仅当someProp变化时,handleScroll才会生成新的实例
  const handleScroll = useCallback(() => {
    // 逻辑里用到了someProp
    console.log(someProp)
  }, [someProp])

  useEffect(() => {
    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, [handleScroll])
}
方案2:用ref保存最新回调,监听器仅挂载/卸载时各操作一次

如果你希望无论回调怎么更新,都不需要反复增删监听器,可以用ref存最新的回调引用:

const Component = ({ someProp }) => {
  const handleScroll = () => {
    console.log(someProp)
  }
  // 用ref存最新的handleScroll
  const scrollHandlerRef = useRef(handleScroll)
  // 每次handleScroll更新时同步到ref
  useEffect(() => {
    scrollHandlerRef.current = handleScroll
  }, [handleScroll])

  // 监听器仅在挂载时注册、卸载时移除,全程只执行两次
  useEffect(() => {
    const listener = () => scrollHandlerRef.current()
    window.addEventListener('scroll', listener)
    return () => window.removeEventListener('scroll', listener)
  }, [])
}

2. 这类场景是否更适合用类组件实现?

完全不需要。
类组件实现相同逻辑时,本质上和函数组件的处理逻辑是一致的:如果你需要在props/state变化时更新绑定的事件回调,一样需要在componentDidUpdate生命周期里执行移除旧回调、添加新回调的操作,并不会比函数组件更简洁。
目前行业内的主流实践是采用函数组件+Hooks处理这类交互场景,类组件属于已经被逐渐淘汰的旧写法,新项目很少会优先选择类组件实现。


内容的提问来源于stack exchange,提问作者grom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 16:09:00