Search code examples
reactjsreact-hookslodash

How to make lodash debounce work with multiple dependencies of useCallback in React


I am using lodash and useCallback to create a memorizable to have more understand how really callback work but stucking. Here is my simple example of my code

import { useCallback, useEffect, useState } from "react";
import "./styles.css";
import { debounce } from "lodash";

const Test = ({ name }) => {
  const [testName, setTestName] = useState("");
  const changeName = () => {
    setTestName((Math.random() + 1).toString(36).substring(7));
  };

  const debounceChangeTestName = useCallback(
    debounce(() => console.log(testName), 2000),
    // 1
    [testName]
  );

  useEffect(() => {
    debounceChangeTestName(testName);
  }, [testName, debounceChangeTestName]);

  useEffect(() => {
    setTestName(name);
  }, [name]);

  return (
    <div>
      {testName} <br />
      <input type="button" onClick={changeName} value="Change" />
    </div>
  );
};

export default function App() {
  return (
    <div className="App">
      <Test name="for test" />
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

Can someone please explain to me 2 questions

  1. if i don't add dependency to debounce function (where i comment 1), why i can't log the value of testName? it's doesn't change in below useEffect?
  2. if i add depencencies to debounce function, how can i log only the last result of testName(in this code above, it's log all of testName value until i stop clicking)

Solution

  • The moment you execute useCallback, the function gets remembered for as long as the dependencies are the same. If you hardcode a variable inside of the function, the executed function will always have the same result. While using your testMessage in the dependency array does the trick, it does not really cause any debounce. Since the debounce is recreated every time the dependency changes, you always get a new instance that does not "remember" the previous call.

    Just pass your testName as a parameter instead of relying on the value in state:

      const debounceChangeTestName = useCallback(
        debounce(msg => console.log(msg), 2000),
        [],
      )
    

    Does that answer your questions?