车轮事件触发多次反应

Wheel event triggers many times react

我正在尝试实现整页滚动。我的问题是 wheel 事件经常触发两次。 我在 vanilla js 上实现了它,而且效果很好。

const [ index, setIndex ] = useState(0);
const sectionWrapper = useRef();
const animationBreak = 750;
const maxIndex = 2;
let lastTime = 0;

useEffect(
    () => {
        const handleWheel = e => {
            const sections = sectionWrapper.current.children;
            const wheelDirection = e.wheelDelta;
            const currentTime = new Date().getTime();
            const isAnimationEnable = currentTime - lastTime > animationBreak;

            if (!isAnimationEnable) return;

            if (wheelDirection < 0 && index < maxIndex) {
                setIndex(index + 1);
            }

            if (wheelDirection > 0 && index > 0) {
                setIndex(index - 1);
            }

            sections[index].scrollIntoView({ behavior: 'smooth' });
            lastTime = currentTime;
        };

        window.addEventListener('wheel', handleWheel);

        return () => window.removeEventListener('wheel', handleWheel);
    },
    [ index ]
);

我尝试使用 lodash 的方法,例如 throttle 或 debounce。不适合我。

您不需要在每次索引更改时删除/重新分配事件处理程序,事件处理代码永远不会更改,您只需要访问最近的 index 状态。您可以通过在 setX 函数上使用回调来执行此操作,例如

useEffect(() => {
  const handleWheel = e => {
    const sections = sectionWrapper.current.children;
    const wheelDirection = e.wheelDelta;
    const currentTime = new Date().getTime();
    const isAnimationEnable = currentTime - lastTime > animationBreak;

    if (!isAnimationEnable) return;

    let index;
    setIndex(i => index = i); // read most recent index
    // use a temp var to get the new state value for scrolling later
    let newIndex;
    if (wheelDirection < 0 && index < maxIndex) {
      setIndex(i => (newIndex = ++i)); // increment the current index by 1
    }

    if (wheelDirection > 0 && index > 0) {
      setIndex(i => (newIndex = --i)); // decrement the current index by 1
    }

    sections[newIndex].scrollIntoView({ behavior: 'smooth' });
    lastTime = currentTime;
  };

  window.addEventListener('wheel', handleWheel);

  return () => window.removeEventListener('wheel', handleWheel);
}, []); // we no longer need any dependences here

useRef 以更简洁的方式解决了这个问题。

    const [ index, setIndex ] = useState(0);
    // using useRef to ref to index
    const indexRef = useRef();
    indexRef.current = index;

    useEffect(() => {
        const handleWheel = e => {
            const sections = sectionWrapper.current.children;
            const wheelDirection = e.wheelDelta;
            const currentTime = new Date().getTime();
            const isAnimationEnable = currentTime - lastTime > animationBreak;

            if (!isAnimationEnable) return;

            if (wheelDirection < 0 && indexRef.current < maxIndex) {
                setIndex(() => indexRef.current + 1);
                sections[indexRef.current].scrollIntoView({ behavior: 'smooth' });
            }

            if (wheelDirection > 0 && indexRef.current > 0) {
                setIndex(() => indexRef.current - 1); 
                sections[indexRef.current].scrollIntoView({ behavior: 'smooth' });
            }

            lastTime = currentTime;
        };

        window.addEventListener('wheel', handleWheel);

        return () => window.removeEventListener('wheel', handleWheel);
    }, []);