React组件useEffect中removeEventListener失效问题求助
问题根源与修复方案
你的问题核心是添加的滚动监听函数和尝试移除的不是同一个引用,导致removeEventListener完全无效,具体问题点和修复步骤如下:
问题拆解
- 你在
addEventListener里传入的是一个匿名箭头函数,但移除时传的是parallax函数——这俩是完全不同的函数引用,浏览器找不到要移除的监听,自然不会生效。 parallax函数每次组件渲染都会重新创建,即使你用它作为监听,后续移除时的parallax已经是新的函数引用了,同样会失败。- 监听用的是
document,移除却用window,虽然浏览器环境下二者的scroll事件本质相通,但这种不一致可能引发隐性问题。
修复后的代码
import { useEffect, useCallback } from 'react'; import { useInView } from 'react-intersection-observer'; const GridTextParallax = () => { const { trigger, inView } = useInView({ threshold: [0, 0.8], }); // 用useCallback包裹,确保函数引用稳定 const parallax = useCallback((scrolling) => { if (scrolling > 0) { // 你的视差逻辑 } }, []); // 抽成具名的滚动处理函数,确保引用唯一 const handleScroll = useCallback(() => { const scrolling = // 计算滚动比例的逻辑 parallax(scrolling); }, [parallax]); useEffect(() => { if (inView) { // 统一用window作为监听目标 window.addEventListener('scroll', handleScroll); // 清理函数:组件卸载或inView变化时移除监听 return () => { window.removeEventListener('scroll', handleScroll); }; } // 当inView变为false时,useEffect会重新执行,此时不会添加监听,也不需要额外移除(因为上一次的清理函数已经执行了) }, [inView, handleScroll]); return ( <section id="layer" ref={trigger} > {/* 前端内容 */} </section> ) } export default GridTextParallax
关键改动说明
- 用
useCallback包裹parallax和handleScroll,保证组件重渲染时函数引用不会改变,确保添加和移除的是同一个函数。 - 将滚动处理逻辑抽成具名函数
handleScroll,替代原来的匿名箭头函数,让addEventListener和removeEventListener使用完全相同的函数引用。 - 统一使用
window作为scroll事件的监听目标,避免环境差异导致的问题。 - 简化
useEffect的逻辑:当inView变为true时添加监听,清理函数负责移除;当inView变为false时,useEffect的依赖变化,会先执行上一次的清理函数移除监听,再执行新的effect(此时inView为false,不会添加新监听)。
内容的提问来源于stack exchange,提问作者Arthur Fortin
相关产品推荐
相关产品推荐

