React函数组件中scroll事件监听函数未注册生效问题咨询
问题原因
你的代码里监听器不生效是两个核心问题导致的,附带还有几个不规范的写法:
- 事件清理逻辑完全写反:
useEffect返回的卸载函数中,你错误地再次调用addEventListener重复绑定事件,正确逻辑应该是调用removeEventListener移除已绑定的事件。这个问题虽然不会导致首次绑定失败,但会造成内存泄漏、事件重复触发的隐患。 - 事件绑定目标错误:你把
scroll事件绑在了h1元素上,但默认的块级标题元素没有设置overflow: scroll/auto样式、自身内容没有超出高度产生滚动条时,元素本身永远不会触发scroll事件。你当前页面的滚动是顶层窗口window产生的,事件绑在没有滚动行为的h1上自然不会执行。 - 附带的不规范问题:
innerRef的TS泛型标注错误,绑定h1元素时对应类型应为HTMLHeadingElement,而非段落元素类型HTMLParagraphElement;state更新函数名拼写错误setScrollPOsition(PO大写),不符合通用命名规范。
修复方案
根据你的实际需求二选一即可:
- 如果需要监听整个页面的滚动,获取元素相对于视口的位置,直接把事件绑定在
window对象上 - 如果需要监听某个内部容器的滚动,给对应容器设置固定高度+
overflow: auto/scroll样式,让容器自身产生滚动条,再绑定事件
页面滚动场景参考代码
import { useState, useRef, useEffect } from 'react'; export default function App() { // 修正函数名拼写 const [scrollPosition, setScrollPosition] = useState(0); // 修正ref泛型类型 const innerRef = useRef<HTMLHeadingElement>(null); const handleScroll = () => { if (!innerRef.current) return; // 获取元素相对于视口的顶部位置 const elementTop = innerRef.current.getBoundingClientRect().top; setScrollPosition(elementTop); }; useEffect(() => { // 绑定window滚动事件 window.addEventListener("scroll", handleScroll); // 清理函数正确移除事件 return () => window.removeEventListener("scroll", handleScroll); }, []); return ( // 给容器加足够高度撑出页面滚动条 <div className="App" style={{ minHeight: '200vh', padding: '2rem' }}> <span className="scrollValue"> h1元素距视口顶部距离:{Math.round(scrollPosition)}px </span> <h1 ref={innerRef} style={{ marginTop: '100px' }}>Find My position on window</h1> <h2>Heading Element</h2> <h2>Heading Element</h2> <h2>Heading Element</h2> </div> ); }
内部容器滚动场景参考代码
import { useState, useRef, useEffect } from 'react'; export default function App() { const [scrollPosition, setScrollPosition] = useState(0); // 此时ref绑定的是滚动容器,类型对应HTMLDivElement const scrollContainerRef = useRef<HTMLDivElement>(null); const handleScroll = (e: Event) => { const target = e.target as HTMLDivElement; setScrollPosition(target.scrollTop); }; useEffect(() => { const container = scrollContainerRef.current; if (!container) return; container.addEventListener("scroll", handleScroll); // 清理函数正确移除事件 return () => container.removeEventListener("scroll", handleScroll); }, []); return ( <div className="App" style={{ padding: '2rem' }}> <span className="scrollValue"> 容器内部滚动距离:{Math.round(scrollPosition)}px </span> {/* 给容器设置固定高度和overflow样式,产生内部滚动条 */} <div ref={scrollContainerRef} style={{ height: '400px', overflow: 'auto', border: '1px solid #eee', marginTop: '20px' }} > <h1>Find My position on window</h1> <h2>Heading Element</h2> <h2>Heading Element</h2> <h2>Heading Element</h2> <h2>Heading Element</h2> <h2>Heading Element</h2> </div> </div> ); }
注意:绑定事件和移除事件的引用必须是同一个函数,不要在绑定位置写匿名函数,否则移除时会找不到对应事件引用,导致清理失效。
内容的提问来源于stack exchange,提问作者Atom
相关产品推荐
相关产品推荐

