【问题标题】:Can't throttle function with useState hook无法使用 useState 挂钩限制功能
【发布时间】:2019-06-21 20:00:20
【问题描述】:

我正在尝试使用 lodash 库为我的 React 应用程序限制滚动时的事件“轮子”,但没有成功。

我需要从滚动输入中收听 e.deltaY 以检测其滚动方向。为了添加一个监听器,我编写了一个 React 钩子,它接受一个事件名称和一个处理函数。

基础实现

  const [count, setCount] = useState(0);

  const handleSections = () => {
    setCount(count + 1);
  };

  const handleWheel = _.throttle(e => {
    handleSections();
  }, 10000);

  useEventListener("wheel", handleWheel);

我的 useEventListener 钩子

function useEventListener(e, handler, passive = false) {
  useEffect(() => {
    window.addEventListener(e, handler, passive);

    return function remove() {
      window.removeEventListener(e, handler);
    };
  });
}

工作演示:https://codesandbox.io/s/throttledemo-hkf7n

我的目标是限制此滚动事件,以减少触发事件并有几秒钟的时间以编程方式滚动我的页面(scrollBy(),类似示例)。 目前节流似乎不起作用,所以我一次得到了很多滚动事件

【问题讨论】:

    标签: javascript reactjs lodash addeventlistener react-hooks


    【解决方案1】:

    当您可以在函数上调用_.throttle() 时,您将返回一个“管理”原始函数调用的新函数。每当调用包装器函数时,包装器都会检查是否经过了足够的时间,如果是,则调用原始函数。

    如果_.throttle() 被多次调用,它会返回一个没有调用该函数的“历史”的新函数。然后它会一次又一次地调用原始函数。

    在您的情况下,包装函数会在每次渲染时重新生成。用useCallback (sandbox) 对_.throttle() 进行调用:

    const { useState, useCallback, useEffect } = React;
    
    function useEventListener(e, handler, cleanup, passive = false) {
      useEffect(() => {
        window.addEventListener(e, handler, passive);
    
        return function remove() {
          cleanup && cleanup(); // optional specific cleanup for the handler
        
          window.removeEventListener(e, handler);
        };
      }, [e, handler, passive]);
    }
    
    const App = () => {
      const [count, setCount] = useState(0);
    
      const handleWheel = useCallback(_.throttle(() => {
        setCount(count => count + 1);
      }, 10000, { leading: false }), [setCount]);
    
      useEventListener("wheel", handleWheel, handleWheel.cancel); // add cleanup to cancel throttled calls
    
      return (
        <div className="App">
          <h1>Event fired {count} times</h1>
          <h2>It should add +1 to cout once per 10 seconds, doesn't it?</h2>
        </div>
      );
    };
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    .App {
      font-family: sans-serif;
      text-align: center;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
    <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    
    <div id="root"></div>

    【讨论】:

    • 试图找到这个疏忽但未能成功,谢谢!一个问题:这是我的滚动事件在 10 秒后再次发生的预期行为吗?目标是每 n 只捕获第一个滚动事件
    • 欢迎。你想在 10 秒后只调用一次处理程序,然后停止?所以如果我滚动 30 秒,计数会在 10 秒后变为 1,然后呢?
    • 感谢你!我希望油门减少我的滚动事件。比如说,在 32 秒的滚动会话中,我会得到 3 个事件(每 10 秒 1 个)
    • 从不阅读所有文档 :D 谢谢,这正是我所需要的!
    • 在删除时添加油门取消,以清除挂起的呼叫。这将防止组件卸载后最后一个油门调用 setState。
    猜你喜欢
    • 2019-05-12
    • 2021-02-19
    • 2021-06-06
    • 1970-01-01
    • 2020-06-10
    • 1970-01-01
    • 2023-01-25
    • 2015-01-06
    • 2020-06-07
    相关资源
    最近更新 更多