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
相关产品推荐
相关产品推荐

