In this article, we will look at how to create a custom debounce hook with React.
React is a JavaScript library used for creating interactive web frontend applications. It is one of the most popular libraries because of its easy-to-use API.
Most recent versions of React provide the Hooks API, which lets us create frontend JavaScript apps with function components. Hooks provides the logic for React function components.
One common feature that is implemented in frontend web apps is adding debounce for inputs. Debouncing lets us delay actions after an input’s value is changed. In this article, we will look at how to create a custom debounce hook with React.
We can create our own custom React hooks based on the basic hooks provided by React.
To do this, we create a function that calls the hooks and returns the values we want. Then we can get the returned values from our custom hook and do what we want with them in our components or other hooks.
For instance, we can make our own hook to get data and return it.
To do this, we write:
import { useCallback, useEffect, useState } from "react";
const useFetch = (url) => {
const [data, setData] = useState();
const [loading, setLoading] = useState();
const getData = useCallback(async () => {
setLoading(true);
const res = await fetch(url);
const json = await res.json();
setData(json);
setLoading(false);
}, [url]);
useEffect(() => {
getData();
}, [getData]);
return { data, loading };
};
to define the useFetch
hook.
A custom hook is just a pure function that uses other hooks. And a pure function is a function that takes some inputs and returns some outputs without committing any side effects.
In our useFetch
hook, we define the data
and loading
states with the useState
hook.
And then we call the useCallback
hook with a function to make a request to the url
with fetch
.
We then call json
to get the JSON response body and call setData
to set the data
state to the json
response object.
Also, we call setLoading
to set the loading
state to true
when the request is going to start and set it to false
when
it’s done.
And then we call useEffect
with a callback that calls getData
to make the request when the getData
function is defined.
Finally, we return the data
and loading
state values in an object.
Once we define the useFetch
hook, we can use it directly in a component.
To do this, we write:
export default function App() {
const { data, loading } = useFetch("https://yesno.wtf/api");
return (
<div className="App">{loading ? "loading" : JSON.stringify(data)}</div>
);
}
We call useFetch
with the URL we want to make the request to.
And we destructure the returned object into the data
and loading
variables.
Then we render the response as a JSON string if loading
is false
and 'loading'
otherwise.
Likewise, we can create our own custom debounce React hook with the same method.
Debouncing will add a delay before we do something. So when we want to debounce an input, we delay setting the value as a state’s value by a short time period.
To do this, we create the useDebounce
hook by writing:
import { useEffect, useRef, useState } from "react";
const useDebounce = (value, delay = 500) => {
const [debouncedValue, setDebouncedValue] = useState("");
const timerRef = useRef();
useEffect(() => {
timerRef.current = setTimeout(() => setDebouncedValue(value), delay);
return () => {
clearTimeout(timerRef.current);
};
}, [value, delay]);
return debouncedValue;
};
Like the useFetch
hook, we create the useDebounce
hook by defining a function with the given name. It takes the input value
and the delay
for the debounce time period in milliseconds. In it, we call the useState
hook to define the debouncedValue
state.
And we call useEffect
with a callback that calls setTimeout
with a callback that calls setDebouncedValue
when value
changes or after a delay
in milliseconds.
The returned timer ID number is assigned to the timerRef
's current
value.
The useRef
hook creates a ref, which is a value that can be kept even after the component or hook is rendered or rerun.
We also return a function that calls clearTimeout
to clear the timer when value
or delay
is changed.
Finally, we return the debouncedValue
so we can use it in our component.
Then, to use useDebounce
in our component, we write:
export default function App() {
const [value, setValue] = useState("");
const debouncedValue = useDebounce(value, 500);
const search = useCallback(async () => {
const res = await fetch(
`https://demo.dataverse.org/api/search?q=${debouncedValue}`
);
const json = await res.json();
console.log(json);
}, [debouncedValue]);
useEffect(() => {
search();
}, [debouncedValue, search]);
return (
<div className="App">
<input value={value} onChange={(e) => setValue(e.target.value)} />
</div>
);
}
We define the value
state with the useState
hook.
Next, we call the useDebounce
hook we created with the value
state and a 500 millisecond delay before returning the debouncedValue
.
And then we define the search
function that makes a get request with fetch
to get data from an API.
We then get the JSON response body with res.json
and then log that value.
The function is used as an argument of the useCallback
hook so we can avoid redefining the function when the debouncedValue
value didn’t change.
Then we call the useEffect
hook with a callback that calls the search
function when debouncedValue
or the search
function
has changed.
Finally, we add an input that sets the value
prop to the value
state and the onChange
prop to a function that calls setValue
to set value
to the input value.
As a result, when we type in something into the input, the debouncedValue
—which is set to value
—is returned after the delay we specified as the second argument of
useDebounce
.
And then the useEffect
callback is called to make the request with the latest debouncedValue
.
One common feature that is implemented in web frontend apps is adding debounce for inputs. Debouncing lets us delay actions after an input’s value is changed.
We can make our own debounce hook so we can easily do some action when we change an input’s value after a delay.
John Au-Yeung is a frontend developer with 6+ years of experience. He is an avid blogger (visit his site at https://thewebdev.info/) and the author of Vue.js 3 By Example.