You might have came across to the situation where you have to make certain api calls depending upon user interactivity. Such as user input in search bar or maybe when user is scrolling through content-loading webpages. Well, that's where debouncing comes into picture ๐ . We cannot call api on every input of the user, I mean obviously we can.. but it won't be efficient ๐ง. Debouncing is a programming practice used to ensure that time-consuming tasks do not fire so often, in order to enhance the performance of the web page. In other words, it limits the rate at which a function gets invoked.
There are may libraries nowadays which provide debouncing functionality like Lodash. We could have use the Lodash but it is too expensive. So let's try to make our own custom hook for debouncing โ๐ป .
We're going to design this hook to debounce the user input and make an api call. The goal is to only have the API call when user stops typing.
import React, { useState, useEffect } from 'react';
import useDebounce from './use-debounce';
// Usage
const App=()=> {
// state for the searched term
const [searchItem, setSearchItem] = useState('');
// state for storing the results
const [results, setResults] = useState([]);
// state for checking if api call is completed
const [isSearching, setIsSearching] = useState(false);
// Now we call our hook, passing in the current searchItem value.
// The hook will only return the latest value (what we passed in) if it's been more than 500ms since it was last called.
// Otherwise, it will return the previous value of searchItem.
const debouncedSearchTerm = useDebounce(searchItem, 500);
// Here's where the API call happens
// We use useEffect since this is an asynchronous action
useEffect(
() => {
// Checks if user has the value
if (debouncedSearchTerm) {
setIsSearching(true);
// Call the api
callSearchApi(debouncedSearchTerm).then(results => {
setIsSearching(false);
setResults(results);
});
} else {
setResults([]);
}
},
// It will only change if the original value (searchItem) hasn't changed for more than 500ms.
[debouncedSearchTerm]
);
return (
<div>
<input
placeholder="Search"
onChange={e => setSearchItem(e.target.value)}
/>
{isSearching && <div>Loading...</div>}
{results.map(result => (
<div key={result.id}>
<h3>{result.name}</h3>
</div>
))}
</div>
);
}
// API search function
const callSearchApi=(search)=> {
return fetch(
`apiendpoint/search?name=search`,
{
method: 'GET'
}
)
.then(res => res.json())
.then(res => res.data.results)
.catch(err => {
console.error(err);
return [];
});
}
Let's build the custom hook to handle debouncing.
import React, { useState, useEffect } from 'react';
export default function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(
() => {
// Seting debouncedValue to value after the specified delay
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
// Return a cleanup function that will be called every time
// useEffect is re-called. useEffect will only be re-called if value changes .
// This is how we prevent debouncedValue from changing if value is changed within the delay period. Timeout gets cleared and restarted.
// Now,if the user is typing in search box, we don't want the debouncedValue to update until they've stopped typing for more than 500ms.
return () => {
clearTimeout(handler);
};
},
[value]
);
return debouncedValue;
}
And that's it, you are good to go ๐๐ป . Now you can use debouncing effect anywhere in your code.