Another frequently asked question in interviews these days is Debouncing
. Debouncing is important concept used in Javascript to improve performance, prevent unnecessary wastage of resource.
Debouncing is a mechanism in which a frequently called function is called once after some threshold time limit is reached.
Lets take example - you have a search input box in UI, now you want to display result on every keyinput by user (similar to how google display search result).
But fetching result from api on every input charater is expensive - there will be network calls, computation on backend and ui update multiple times.
If a user want to search for baking receipe
then it comes to total 14 characters l.e 14 api call will be made to backend, but out of 14, only 1 or 2 are enough to display relevant result.
so on an average you make 12 api call which are not required, and if your application have 1000s of user then you multiply wasted api calls accordingly. Because resource wastage is on backend side, so 12 * 1000 api calls, this is only one search query. If your average user is making 10 search query on a day = 12 * 1000 * 10. You can see where i am going with this.
We can avoid making unnecessary api calls by using debouncing. We design frontend UI in a way that on every character input, system wait for some time (it can be 1 second or 500 millisecond depending on your application), and during this time if there is another input then we wait again and only make api call (with complete user input) if there is no further input.
Implementation -
Consider a React application
const HomeScreen = () => {
const [searchQuery, setSearchQuery] = useState("")
const triggerSearch = (e) => {
setSearchQuery(e.target.value)
fetchResult(e.target.value)
}
const fetchResult = () => {
console.log("API called for following input :: ", input)
}
return (
<div className="App">
<label>Search for: </label>
<input value={searchQuery} onInput={triggerSearch}/>
</div>
)
}
If we type for baking receipe the fetchResult function get called 14 times
Solution :
const HomeScreen = () => {
const [searchQuery, setSearchQuery] = useState("")
const timer = useRef();
const triggerSearch = (e) => {
setSearchQuery(e.target.value)
debouncedFetchResult(e.target.value)
}
const fetchResult = (input) => {
console.log("API called for following input :: ", input)
}
const debouncedFetchResult = (input) => {
if (timer.current) {
// if there was any pending api call then cancel it and request new
clearTimeout(timer.current)
}
// here we are waiting for new input for 1second. you can change accordingly
let timeoutInstance = setTimeout(() => {
fetchResult(input)
},1000)
timer.current = timeoutInstance;
}
return (
<div className="App">
<label>Search for: </label>
<input value={searchQuery} onInput={triggerSearch}/>
</div>
)
}
Here, on every input character, we call setTimeout
and store its reference inside state variable. And if there is any old reference available then we cancel old setTimeout
and create a new only.
Top comments (5)
Don't store the timeout in a state variable though. Better to use a ref. There's no need to rerender the component each time the debounce is triggered, which is what happens if you use a state variable.
@brense - Good point. Don't know how i missed it. Will update code soon.
Thanks
You should add
javascript
after the three backticks to get syntax highlighting.But also there's a lot of issues with this debounce code. I'd refactor it into a single hook via
useEffect
, or just use a third party library until you learn react better.Thanks for the formatting tip.
Idea is to explain it in simple approach or enable young engineers to write their version from scratch instead of relying on any 3rd party library.
Regarding implementation via
useEffect
- that's another approach, better than this one also. Will share it in comments as well.Here, we are relying on useEffect's cleanup function to clear any pending api call.