Lodash debounce with React Input
Solution 1
The debounce function can be passed inline in the JSX or set directly as a class method as seen here:
search: _.debounce(function(e) {
console.log('Debounced Event:', e);
}, 1000)
Fiddle: https://jsfiddle.net/woodenconsulting/69z2wepo/36453/
If you're using es2015+ you can define your debounce method directly, in your constructor
or in a lifecycle method like componentWillMount
.
Examples:
class DebounceSamples extends React.Component {
constructor(props) {
super(props);
// Method defined in constructor, alternatively could be in another lifecycle method
// like componentWillMount
this.search = _.debounce(e => {
console.log('Debounced Event:', e);
}, 1000);
}
// Define the method directly in your class
search = _.debounce((e) => {
console.log('Debounced Event:', e);
}, 1000)
}
Solution 2
With a functional react component try using useCallback
. useCallback
memoizes your debounce function so it doesn't get recreated again and again when the component rerenders. Without useCallback
the debounce function will not sync with the next key stroke.
`
import {useCallback} from 'react';
import _debounce from 'lodash/debounce';
import axios from 'axios';
function Input() {
const [value, setValue] = useState('');
const debounceFn = useCallback(_debounce(handleDebounceFn, 1000), []);
function handleDebounceFn(inputValue) {
axios.post('/endpoint', {
value: inputValue,
}).then((res) => {
console.log(res.data);
});
}
function handleChange (event) {
setValue(event.target.value);
debounceFn(event.target.value);
};
return <input value={value} onChange={handleChange} />
}
`
Solution 3
This is how I had to do it after googling the whole day.
const MyComponent = (props) => {
const [reload, setReload] = useState(false);
useEffect(() => {
if(reload) { /* Call API here */ }
}, [reload]);
const callApi = () => { setReload(true) }; // You might be able to call API directly here, I haven't tried
const [debouncedCallApi] = useState(() => _.debounce(callApi, 1000));
function handleChange() {
debouncedCallApi();
}
return (<>
<input onChange={handleChange} />
</>);
}
Solution 4
A lot of the answers here I found to be overly complicated or just inaccurate (i.e. not actually debouncing). Here's a straightforward solution with a check:
const [count, setCount] = useState(0); // simple check debounce is working
const handleChangeWithDebounce = _.debounce(async (e) => {
if (e.target.value && e.target.value.length > 4) {
// TODO: make API call here
setCount(count + 1);
console.log('the current count:', count)
}
}, 1000);
<input onChange={handleChangeWithDebounce}></input>
Solution 5
That's not so easy question
On one hand to just work around error you are getting, you need to wrap up you setVariables
in the function:
search(e){
let str = e.target.value;
_.debounce(() => this.props.relay.setVariables({ query: str }), 500);
}
On another hand, I belive debouncing logic has to be incapsulated inside Relay.
Michael Kaufman
Updated on October 04, 2021Comments
-
Michael Kaufman over 2 years
I'm trying to add debouncing with lodash to a search function, called from an input onChange event. The code below generates a type error 'function is expected', which I understand because lodash is expecting a function. What is the right way to do this and can it be done all inline? I have tried nearly every example thus far on SO to no avail.
search(e){ let str = e.target.value; debounce(this.props.relay.setVariables({ query: str }), 500); },
-
Michael Kaufman about 8 yearsThanks for that. What I'm seeing now is a console log of the synthetic event and I need the e.target.value to perform the search.. I've tried e.persist() but it doesn't seem to do anything. Debouncing is technically working but without passing it a value it's not working. Thanks for any help.
-
Michael Kaufman about 8 yearsI couldn't use that exactly, but it got me where I needed to go. I basically had the input call search(e) and then passed that event to another function with the debouncing. I read about event.persist() but I couldn't get that work. Thanks for your help!!
-
Ezeewei over 6 years@Jeff Wooden fidden is broken
-
Rajesh Mbm about 6 yearsthanks for suggesting componentWillMount. able to access props function as well in debounced function. if i put inside constructor, somehow i am not able to access props functions.
-
Jeff Wooden about 6 years@RajeshMbm You can access props inside of a class constructor, see the updated example - it's available as the first argument (make sure to include the super call).
-
CuteShaun almost 4 yearsuseEffect trigger only one time, because reload after first call will be always true.
-
CuteShaun almost 4 yearsTry set your value to handleChange, than debouncedCallApi, then callApi -> state, after this useEffect trigger your function ^_^
-
ProblemSolver about 3 yearsi think it calls n no of times , and just applies a delay as such, times , i tried it now at least.
-
Indigo about 3 yearsIt is better to explain your answer, what it is doing, how it is solving OP's problem, give some references as required. Just adding a code block or a link is not enough. Check StackOverflow answer guide
-
Juanma Menendez almost 3 yearsyou have a typo in the import: import _debouce from 'lodash/debounce'; It is NOT _debouce IT IS _debounce
-
Ashfaq nisar over 2 yearsThank you jules!
-
Fiddle Freak over 2 yearsit would be nice if we could see a solution using react hooks.
-
Fiddle Freak over 2 yearsjust a warning,
useCallback
will set all variables withuseState
to the initial value of the page loaded. I ran into this bug, and am now trying to find another way to use the debouncer. -
Fiddle Freak over 2 yearsI would avoid using
useCallback
with the debouncer. This will force alluseState
hooks to reset to the initial state from when the page was loaded. -
Jules Patry over 2 years@FiddleFreak I have never experienced that. I think the issue is something else.
-
Fiddle Freak over 2 years@JulesPatry I actually got around that problem by doing this instead > stackoverflow.com/questions/70501416/…