传递一个参数给函数回调

问题描述 投票:0回答:1

我试图使用debounce在lodash延迟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} />;
}

一开始,我试图使用 searchValue 但似乎由于范围的原因,它无法读取,虽然状态已经更新,但总是空的。

结果,我试着把它从 debounceLoadData 但我不知道如何才能做到这一点,因为useCallback中的内容是一个函数调用。我怎样才能将 searchValuefetchData 里面 debounce.

javascript reactjs typescript lodash
1个回答
3
投票

lodash debounce将一个函数作为第一个参数。你可以简单地使用 fetchData 作为函数,并传递给 searchValuedebounceLoadData 然后将其传递给 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 只创建一次,因为回调依赖关系是 []无论你是否将它作为一个依赖传递给useEffect,都不会有任何区别。

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

当使用useEffect React Hook时,如何修复缺失的依赖性警告?


1
投票

我从来不喜欢 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]);

  // ...
}

然而对于你的情况,我不认为使用lodashdebounce是最好的解决方案。

有一个隐藏的风险,即最后调用的效果。fetchData 发生在你的组件被卸载之后。而如果 fetchData 包含一些状态突变逻辑,这会引发 "Can't call setState (or forceUpdate) on an unmounted component. "的错误,这不是破坏性的,但也不是最优的。

我建议使用 setTimeout/clearTimeout. 这是非常简单的。

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

0
投票

我想你是被功能上的混淆了 setState 语法。试试这个。

const debounceLoadData = useCallback(() => debounce(() => fetchData(searchValue), 1000), []);
© www.soinside.com 2019 - 2024. All rights reserved.