APRIL 4, 2024 3 min read
Mastering Debouncing in React.js: From Scratch, Lodash & Custom Hooks to Boost App Responsiveness
Explore three methods to implement debouncing in React.js: manual implementation, the Lodash library, and custom hooks with awesome-debounce-promise. Optimize performance by delaying function calls during rapid user interactions like search inputs or window resizing.
When interacting with a web application, we might fire bursting events such as entering text in a search field, resizing a window, scrolling, or dragging an element on the screen. These events are repeatedly invoked, which significantly decreases the website's performance by making it laggy, and sometimes unresponsive. To handle such situations, debouncing comes to the rescue.
Debouncing is a programming technique that limits the call to a function or an event. Debounced functions only run after a specified period and not when they are invoked.
There are many ways to achieve debouncing in JavaScript. We are going to implement it in the following ways.
- Implementing from scratch
- Using lodash
- Using custom hooks
Let’s get started
Implementing from scratch
We start by creating a function that returns another function. This function handles all the logic of calling the function passed as a parameter when the specified time is passed.
const debounce = (func, delay) => {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => {
clearTimeout(timer);
func(...args);
}, delay);
};
};The function defined above is a higher-order function that accepts a function as an argument and returns a function. With this, we create a closure around the func, delay and the timer variable so they can preserve the value when the parent function is executed and removed from the call stack.
Debouncing using lodash library
Another way we can achieve debouncing is to use a library called lodash. We can make use of the debounce method which is provided by lodash.
npm install lodash
import { debounce } from "lodash"We simply need to wrap the callback function with the debounce method and provide the delay as a second parameter.
const handleValueChange = debounce(( value ) => {
setValue( value );
}, 500);Using custom hook for debouncing
We can make our own implementation of debouncing using a custom hook. We would use
We can use npm or yarn to install the packages.
npm install awesome-debounce-promise react-async-hook use-constantImport the packages into react application
import useConstant from "use-constant";
import { useAsync } from "react-async-hook";
import AwesomeDebouncePromise from "debounce-promise";We are making use of use-constant package as this aids us in executing the function once and store the returned value in a constant.
import { useState } from "react";
import useConstant from "use-constant";
import { useAsync } from "react-async-hook";
import AwesomeDebouncePromise from "debounce-promise";
const useDebounce = (func, delay) => {
const [text, setText] = useState("");
const debouncedSearchFunction = useConstant(() =>
AwesomeDebouncePromise(func, delay)
);
const searchResults = useAsync(async () => {
if (text.length === 0) {
return [];
} else {
return debouncedSearchFunction(text);
}
}, [debouncedSearchFunction, text]);
return {
text,
setText,
searchResults
};
};
export default useDebounce;Let’s dive deep into the code.
- We import the packages and declare a state variable to store the text.
useConstanthook is called, which accepts a function as a parameter.useConstanthelps in executing this function only once. We can use React’suseMemobut it won’t guarantee that the function is only called once.- Inside the function, we are using
AwesomeDebouncePromisewhich debounces async calls. This method accepts a function which is to be debounced, and a timer which indicates the time after which the function is invoked. - On line 13, we pass a callback function to
useAsyncalong withdebouncedSearchFunctionandtextin the dependency array. Every time thetextchanges, it calls the callback function, but as it is debounced, it is not invoked frequently. - Lastly, we return the
text,setText, andsearchResultsfrom the hook.
Let’s move on to app.js where are going to use the hook we just implemented.
import React, { useState } from "react";
import useDebounce from "./useDebounce";
const App = () => {
const [value, setValue] = useState("");
const { text, setText } = useDebounce(
(text) => handleValueChange(text),
1000
);
const handleValueChange = (value) => {
setValue(value);
};
return (
<>
<h2 style={{ textAlign: "center" }}>Using custom hook for debouncing</h2>
<input
type="text"
className="search"
placeholder="Type here..."
value={text}
onChange={(e) => setText(e.target.value)}
/>
<p>You entered: {value}</p>
</>
);
};
export default App;- Import the hook created earlier.
- Pass a function along with the timer to the
useDebouncehook. This hook returnstext,setText, andsearchResultswhich are destructured. - When we type something into the input field,
onChangeis fired where we passe.target.valuetosetText.
Conclusion
We have seen how we can implement debouncing using three different techniques. We start with simple implementation from scratch, followed by debouncing with lodash, and finally writing our custom hook.
If you have any queries, let me know in the comment section.
Happy coding !!!