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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 11:18:15