简体   繁体   中英

Pass a argument to a function callback

I'm trying to use debounce in lodash to delay the onChange, see the code below.

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} />;
}

In the beginning, I was tring to use searchValue in fetchData function but seems because of scope, it failed to read it, it was always empty though the state had been updated.

As a result, I try to pass it in from the debounceLoadData but I don't know how I can do that as what in useCallback is a function invocation. How can I pass searchValue in fetchData inside debounce .

lodash debounce takes in a function as the first argument. You can simply use fetchData as the function and pass on the searchValue to debounceLoadData which will then be passed to fetchData on invocation

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

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

debounce actually returns a function, think of debounce as being implemented like

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

So basically debounceLoadData is the returned function here and arguments passed to it ie..args are being then passed to original function fetchData like func.apply(context, args)

Also debounceLoadData is created only once as the callback dependency is [] , you whether you pass it to useEffect as a dependency of not will not make any difference.

Please read this post for missing dependency warning

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

I never like useCallback , it's quite a confusing hook, and I always use useMemo instead since it totally covers what useCallback can do (but not the other way around).

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]);

  // ...
}

Yet for your case I don't think using lodash debounce is the best solution.

There's a hidden risk that the final invocation of effect fetchData happens AFTER your component is unmounted. And if fetchData contains some state mutation logic, that would raise an error of "Can't call setState (or forceUpdate) on an unmounted component." which is not destructive but not optimal either.

I suggest manually debounce call using setTimeout/clearTimeout . It's pretty simple:

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

I think you are getting confused by the functional setState syntax. Try this:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM