skip to Main Content

I found this code from this answer https://stackoverflow.com/a/68780417/26420821

How does it work?

export function useSearchDebounce(delay = 350) {
  const [search, setSearch] = useState(null);
  const [searchQuery, setSearchQuery] = useState(null);

  useEffect(() => {
    const delayFn = setTimeout(() => {
        console.log("setTimeout called");
        setSearch(searchQuery);
    }, delay);

    return () => clearTimeout(delayFn);
  }, [searchQuery, delay]);

  return [search, setSearchQuery];
}

Usage :

const [search, setSearch] = useSearchDebounce();

<input onChange={(e) => setSearch(e.target.value)}/>

If we assume user types "abc" together in the input field with delay set to 5000,

At first, searchQuery will be "a" , it will setTimeout() to run after 5 secs,

Then searchQuery will be "ab" , it will again setTimeout() to run after 5 secs,

Then searchQuery will be "abc" , it will again setTimeout() to run after 5 secs

But when I tested console.log() executed just once, Why didn’t setTimeout() execute 3 times ?

Am I misunderstanding something ?

I don’t understand the code

2

Answers


  1. But when I tested console.log() executed just once, Why didn’t
    setTimeout() execute 3 times ?

    Because every time one of your dependencies change (ie: searchQuery or delay), the cleanup function is first run clearing the previous timeout that had been scheduled, then it runs your useEffect callback to schedule a new timeout.

    So the behaviour you’re missing is that the previous timeout is cleared when the user types "abc":

    • searchQuery will be "a" , clear previous timeout (if one exists, one technically gets scheduled onmount), and schedule a new setTimeout() to run after 5 secs
    • searchQuery will be "ab" , clear previous timeout (so that never executes its callback now), and schedule a new setTimeout() to run after 5 secs
    • searchQuery will be "abc" , clear previous timeout (so that never executes its callback now), and schedule a new setTimeout() to run after 5 secs

    Timeout completes as nothing clears it and you see the setTimeout called log.

    Login or Signup to reply.
  2. 
    export function Filter() {
      const [filter, setFilter] = useState("");
      const debounced = useDebounce(filter);
    
      return (
        <>
          <input value={filter} onChange={(e) => setFilter(e.target.value)} />
          <div>{`Debounced Value: ${debounced}`}</div>
        </>
      );
    }
    
    function useDebounce<T>(value: T, delay = 300) {
      const [debounced, setDebounced] = useState<T>(value);
    
      useEffect(() => {
        const timeoutId = setTimeout(() => {
          setDebounced(value);
        }, delay);
    
        return () => {
          clearTimeout(timeoutId);
        };
      }, [delay, value]);
    
      return debounced;
    }
    

    By separating the input element state and the debounced value you get access to both:

    • the immediate updating of the input element
    • the delayed update of the debounced value (div element in example)
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search