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

React中不依赖外部库实现onScroll事件防抖的方法

问题:scroll事件重复触发导致多次打印的解决办法

问题描述

给滚动容器绑定onScroll事件后,即便仅触发一次keydown事件(经monitorEvents()验证),控制台仍会多次打印scrolled。要求在不依赖外部库的前提下,确保每次滚动操作仅触发一次打印,且必须通过监听scroll事件实现(不改用keydown)。

原代码:

import "./App.css";

function App() {
  return (
    <section
      onClick={() => console.log("clicked")}
      onScroll={() => console.log("scrolled")}
      style={{ height: "100vh", width: "100vw", overflow: "scroll" }}
    >
      <p style={{ height: "300vh" }}>hi</p>
    </section>
  );
}

export default App;

解决方案

方案1:防抖(Debounce)—— 滚动停止后触发一次

防抖的核心逻辑是:每次触发scroll事件时清除之前的定时器,重新计时,只有当滚动停止超过设定时长(比如100ms),才执行打印操作。这样一次完整的滚动动作(比如按一次方向键后滚动至停止)只会触发一次打印。

修改后的代码:

import "./App.css";
import { useCallback, useRef } from "react";

function App() {
  // 保存定时器ID,避免组件重渲染丢失引用
  const debounceTimerRef = useRef(null);

  // 缓存防抖后的回调函数,防止不必要的重渲染
  const handleScroll = useCallback(() => {
    if (debounceTimerRef.current) {
      clearTimeout(debounceTimerRef.current);
    }
    // 滚动停止100ms后执行打印
    debounceTimerRef.current = setTimeout(() => {
      console.log("scrolled");
    }, 100);
  }, []);

  return (
    <section
      onClick={() => console.log("clicked")}
      onScroll={handleScroll}
      style={{ height: "100vh", width: "100vw", overflow: "scroll" }}
    >
      <p style={{ height: "300vh" }}>hi</p>
    </section>
  );
}

export default App;

方案2:节流(Throttle)—— 滚动过程中仅触发一次

如果需要在滚动开始时立即触发打印,之后在设定间隔内不再重复触发,可以使用节流。核心逻辑是记录上次触发的时间戳,每次触发scroll事件时判断是否超过设定间隔,超过则执行打印并更新时间戳。

修改后的代码:

import "./App.css";
import { useCallback, useRef } from "react";

function App() {
  // 记录上次触发的时间戳
  const lastTriggerTimeRef = useRef(0);

  const handleScroll = useCallback(() => {
    const now = Date.now();
    // 设定200ms的触发间隔,间隔内不重复执行
    if (now - lastTriggerTimeRef.current > 200) {
      console.log("scrolled");
      lastTriggerTimeRef.current = now;
    }
  }, []);

  return (
    <section
      onClick={() => console.log("clicked")}
      onScroll={handleScroll}
      style={{ height: "100vh", width: "100vw", overflow: "scroll" }}
    >
      <p style={{ height: "300vh" }}>hi</p>
    </section>
  );
}

export default App;

选择建议

  • 若需要滚动完全停止后再触发打印,优先选防抖方案,可根据实际场景调整定时器时长(比如100ms~300ms)。
  • 若需要滚动开始时立即触发一次打印,之后不再重复触发,选节流方案,间隔时长可按需设置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 00:04:57