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

React中如何让wheel事件每次滚动仅触发一次?

解决React中wheel事件多次触发的问题

你的问题主要来自两个点:一是wheel事件本身的特性——不管是滚轮还是触控板,一次滚动动作会连续触发多次wheel事件;二是你的useEffect依赖了trackpadSwipe和pageNumber,每次状态更新都会重新绑定事件监听器,导致多个监听器同时生效,触发次数翻倍。

直接给你修改后的代码,附关键改动说明:

import { useEffect, useState, useCallback } from 'react';

// 防抖函数:指定时间内只执行一次回调
const debounce = (func, delay) => {
  let timeoutId;
  return (...args) => {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => func.apply(this, args), delay);
  };
};

function YourComponent() {
  const [pageNumber, setPageNumber] = useState(1);
  // 若没有其他业务依赖,可直接去掉trackpadSwipe状态
  // const [trackpadSwipe, setTrackpadSwipe] = useState('none');

  // 用useCallback缓存处理函数,避免每次渲染重新创建
  const handleTrackpadSwipe = useCallback(debounce((event) => {
    // 忽略微小滚动,过滤误触
    if (Math.abs(event.deltaY) < 10) return;

    if (event.deltaY > 0) {
      console.log('swiped down');
      // setTrackpadSwipe('swiped down');
      if (pageNumber < 4) {
        // 函数式更新确保拿到最新状态值,避免闭包问题
        setPageNumber(prev => prev + 1);
      }
      // setTrackpadSwipe('none');
    } else if (event.deltaY < 0) {
      console.log('swiped up');
      // setTrackpadSwipe('swiped up');
      if (pageNumber > 1) {
        setPageNumber(prev => prev - 1);
      }
      // setTrackpadSwipe('none');
    }
  }, 300), [pageNumber]); // 300ms防抖间隔,可按需调整

  useEffect(() => {
    window.addEventListener('wheel', handleTrackpadSwipe);
    
    // 必须返回清理函数,避免重复绑定监听器
    return () => {
      window.removeEventListener('wheel', handleTrackpadSwipe);
    };
  }, [handleTrackpadSwipe]);

  return (
    <div style={{ overflow: 'hidden', height: '100vh' }}>
      当前页码:{pageNumber}
    </div>
  );
}

export default YourComponent;

关键改动说明:

  • 防抖函数过滤重复事件:通过debounce限制一段时间内只处理一次滚动,过滤掉触控板/滚轮连续触发的多余事件。
  • 清理事件监听器:useEffect返回清理函数,组件卸载或依赖变化时移除旧监听器,避免重复绑定。
  • 函数式更新状态:用setPageNumber(prev => prev + 1)替代直接依赖pageNumber,解决闭包导致的状态值滞后问题。
  • 缓存处理函数:通过useCallback缓存防抖后的处理函数,减少不必要的useEffect触发。
  • 移除冗余状态操作:原代码中setTrackpadSwipe刚设置就清空,若无其他业务依赖可直接删除。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 21:50:16