【问题标题】:Pass a argument to a function callback将参数传递给函数回调
【发布时间】:2020-05-06 05:29:54
【问题描述】:

我正在尝试在 lodash 中使用 debounce 来延迟 onChange,请参见下面的代码。

import React, { useState, useEffect, useCallback } from "react";
import { TopBar } from "@shopify/polaris";
import { debounce } from "lodash";

function SearchBar() {
  const [searchValue, setSearchValue] = useState("");

  const handleSearchFieldChange = ((value:string) => {
    setSearchValue(value);
  });

  const debounceLoadData = useCallback(debounce({searchValue} => fetchData, 1000), []);

  useEffect(() => {
    debounceLoadData();
    console.log({searchValue})
  }, [searchValue]);

  function fetchData(value:string) {
    console.log("searchValue " + value);
  }

  const searchFieldMarkup = (
    <TopBar.SearchField
      onChange={handleSearchFieldChange}
      value={searchValue}
      placeholder="Search Value"
    />
  );

  return <TopBar searchField={searchFieldMarkup} />;
}

一开始,我想在 fetchData 函数中使用searchValue,但似乎因为作用域的原因,它无法读取它,虽然状态已经更新,但它总是为空。

因此,我尝试从 debounceLoadData 传递它,但我不知道如何做到这一点,因为 useCallback 是一个函数调用。我怎样才能在fetchData 中通过searchValuedebounce 中。

【问题讨论】:

  • 也许您应该先让fetchData 函数工作,不要进行任何去抖动或节流,然后再确定是否需要提高性能。

标签: javascript reactjs typescript lodash


【解决方案1】:

lodash debounce 接受一个函数作为第一个参数。您可以简单地使用fetchData 作为函数并将searchValue 传递给debounceLoadData,然后在调用时将其传递给fetchData

const debounceLoadData = useCallback(debounce(fetchData, 1000), []);

  useEffect(() => {
    debounceLoadData(searchValue);
    console.log({searchValue})
  }, [searchValue]);

debounce 实际上是返回一个函数,把 debounce 想象成是像

那样实现的
function debounce(func, wait) {
  let timeout
  return function(...args) {
    const context = this
    clearTimeout(timeout)
    timeout = setTimeout(() => func.apply(context, args), wait)
  }
}

所以基本上debounceLoadData 是这里返回的函数,arguments passed to it i.e ...args 然后被传递给原始函数 fetchData,如func.apply(context, args)

同样debounceLoadData只被创建一次,因为回调依赖是[],你是否将它作为not的依赖传递给useEffect不会有任何区别。

请阅读这篇文章以了解缺少依赖警告

How to fix missing dependency warning when using useEffect React Hook?

【讨论】:

  • 值传递是如何工作的?我想通了,但我不明白。我还在控制台React Hook useEffect has a missing dependency: 'debounceLoadData'. Either include it or remove the dependency array react-hooks/exhaustive-deps 中看到了一个输出
  • 更新了我的答案
  • @user1888955 请记住一件事,就函数签名而言,debounce 中的内容是相同的。 debounce 只需将延迟的超能力添加到该功能。功能本身保持不变。例如。 debounce(x =&gt; x +1) 输出相同的功能x =&gt; x + 1
【解决方案2】:

我从不喜欢useCallback,它是一个相当令人困惑的钩子,我总是使用useMemo 代替,因为它完全涵盖了useCallback 可以做什么(但不是相反)。

function SearchBar() {
  const [searchValue, setSearchValue] = useState("");

  const handleSearchFieldChange = ((value:string) => {
    setSearchValue(value);
  });

  const debounceLoadData = useMemo(() => debounce(fetchData, 1000), []);
  /**
   * the equivalent of useCallback should be:
   *
   * const debounceLoadData = useCallback(debounce(fetchData, 1000), []);
   * 
   * But I really advice against it!
   * There's unnecessary function invocation compared to useMemo.
   */


  useEffect(() => {
    debounceLoadData(searchValue);  // <- you should pass in arg
    console.log({searchValue})
  }, [searchValue]);

  // ...
}

但对于您的情况,我不认为使用 lodash debounce 是最好的解决方案。

存在一个隐藏的风险,即在卸载组件之后最终调用效果 fetchData。如果fetchData 包含一些状态突变逻辑,则会引发“无法在未安装的组件上调用 setState(或 forceUpdate)”的错误。这不是破坏性的,但也不是最优的。

我建议使用setTimeout/clearTimeout 手动去抖动呼叫。很简单:

useEffect(() => {
  const timeoutId = setTimeout(() => fetchData(searchValue), 1000)
  return () => clearTimeout(timeoutId)
}, [searchValue])

【讨论】:

  • 我在使用 useCallback 时标记了 @ShubhamKhatri 的答案。但我认为你只是使用 setTimeout 是对的。
  • 很好,很高兴能帮上忙。
【解决方案3】:

我认为您对功能性setState 语法感到困惑。试试这个:

const debounceLoadData = useCallback(() => debounce(() => fetchData(searchValue), 1000), []);

【讨论】:

  • 不,您将 useCallback 与 useMemo 混淆了。您的代码用于 useMemo。
猜你喜欢
  • 1970-01-01
  • 2011-03-28
  • 2011-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多