在 React es6 中去抖动

Debounce in React es6

我的一个页面中有一个搜索输入,我试图确保它不会在用户键入速度过快的情况下在一秒钟内发出 10 个请求。

似乎去抖动是可行的方法。我已经阅读了多篇博客文章和 SO 问题。我正在尝试使用 lodash 的去抖动。我一定是做错了什么,因为发生的事情是我所有的电话都通过了,只是稍后。

这是我的组件代码:

const Partners = (props: any) => {
  const [query, setQuery] = useState("");
  const { partners, totalPages, currentPage } = props.partnersList;

  useEffect(() => {
    props.getPartners();
  }, []);

  useEffect(() => debounce(() => props.getPartners(query), 10000), [query]);

  const handleQueryChange = (e: any) => {
    setQuery(e.target.value);
  };

  const handlePageChange = (e: React.ChangeEvent<unknown>, value: number) => {
    props.getPartners(query, value);
  };

  return (
    <React.Fragment>
      <Sidebar />
      <MainContent className="iamSearchPartners">
        <h1>Partners</h1>
        <TextField
          className="iamSearchPartners__search_input"
          variant="outlined"
          size="small"
          onChange={handleQueryChange}
          value={query}
        />
        <PartnersList partners={partners} />
        {totalPages > 1 && (
          <Pagination
            currentPage={currentPage || 1}
            totalPages={totalPages}
            handlePageChange={handlePageChange}
          />
        )}{" "}
      </MainContent>
    </React.Fragment>
  );
};

如您所见,我有一个 useEffect 监听查询的更改。

老实说,我最终还是手动完成了这件事。查看 debouncing and throttling 之间的差异。我想你想限制请求。如果你去抖动,在计时器结束之前什么都不会发生。

如果您想像您的示例中那样等待 10 秒,那么如果用户至少每 10 秒键入一次,直到最后一次键入 10 秒后才不会发生任何事情。与限制请求每 10 秒发出一次相反。

这种方法是一种混合方法,因为我们要确保最后一个仍然熄灭(就像去抖动一样),而节流可能不会,但我们仍在用户键入时发送请求。

  const timer = useRef<number>(0);
  const lastEventSent = useRef(Date.now());

  useEffect(() => {
     if (Date.now() - lastEventSent.current < 500) {

     // The event was fired too recently, but we still want
     // to fire this event after the timeout if it is the last
     // one (500ms in this case).
      timer.current = setTimeout(() => {
          lastEventSent.current = Date.now();
          props.getPartners(query)
      }, 500);

     } else {

       // An event hasn't been fired for 500ms, lets fire the event
       props.getPartners(query)
       lastEventSent.current = Date.now();

       // And if the user was typing, there is probably a timer, lets clear that
       clearTimeout(timer.current);
     }
  
     // Cleanup the timer if there was one and this effect is reloaded.
     return () => clearTimeout(timer.current);

   }, [query]);

debounce 创建作为参数传递的函数的去抖动版本。在这种特定情况下,在 useEffect 中调用它会在每个渲染器上创建一个新的去抖动版本的函数。

为了缓解这种情况,可以在渲染函数之外创建去抖动版本,因此不会在每次渲染时都重新创建。

const myDebouncedFunction = debounce((handler, query) => handler(query), 10000);

const Partners = (props: any) => {
  // omitted ...
  useEffect(() => {
    myDebouncedFunction(props.getPartners, query);
  }, [query]);
  // omitted ...

useMemo 或将其放入 useState 中也可以。

另一件事:useEffect 只调用了 returned 去抖动版本,因为它是一个没有大括号的箭头函数,使它成为 return 评估的结果。 useEffect 利用其功能的 return 成为某种“取消订阅”处理程序,see React docs about "Effects with cleanup"。所以每当 query 改变时(第二次之后),效果就是调用函数。每次查询更改时都应调用上面的版本。

就个人而言,我会尝试在 handleQueryChange 中而不是 useEffect 中处理函数的去抖动版本的调用,以使“它应该何时发生”更加清晰。