I have the following setup in a react component where I have used the debounce function from throttle-debounce package to log the input value for testing ( actually calling an API to fetch data ).
import React, { useState, useRef } from 'react';
import debounce from 'throttle-debounce/debounce';
const MyComponent = () => {
const [localSearchQuery, setLocalSearchQuery] = useState('');
const setSearchQueryInParams = debounce(2000, value => console.log(value))
const setSearchQuery = value => {
setLocalSearchQuery(value);
setSearchQueryInParams(value);
};
return (
<>
<Input
value={localSearchQuery}
onChange={setSearchQuery}
/>;
</>
);
};
But, it's not working as expected. If I type hello in the input box, I get the following output in console:
h
he
hel
hell
hello
However, if I wrap the debounce function with useRef, it works as expected
const setSearchQueryInParams = useRef(debounce(2000, value => console.log(value))).current
Input: hello
Output in console: hello
My question is, why the debounce function is not working without useRef and how useRef is functioning here to get the desired output?
Thank you for the help.
You'll want to use useCallback, with an empty dependency array, nstead of useRef:
import React, { useState, useCallback } from 'react';
import debounce from 'throttle-debounce/debounce';
const MyComponent = () => {
const [localSearchQuery, setLocalSearchQuery] = useState('');
const setSearchQueryInParams = useCallback(debounce(2000, value => console.log(value)), []);
const setSearchQuery = value => {
setLocalSearchQuery(value);
setSearchQueryInParams(value);
};
return (
<>
<Input
value={localSearchQuery}
onChange={setSearchQuery}
/>;
</>
);
};
The reason is that calling debounce always returns a new function, so you need to store that function across rerenders.
Without that, it has no memory of the setTimeouts it internally calls so it can't use them to debounce properly.