← Back

Mastering Debouncing in React.js: From Scratch, Lodash & Custom Hooks to Boost App Responsiveness

April 4, 2024

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-constant

Import 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.
  • useConstant hook is called, which accepts a function as a parameter. useConstant helps in executing this function only once. We can use React’s useMemo but it won’t guarantee that the function is only called once.
  • Inside the function, we are using AwesomeDebouncePromise which 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 useAsync along with debouncedSearchFunction and text in the dependency array. Every time the text changes, it calls the callback function, but as it is debounced, it is not invoked frequently.
  • Lastly, we return the text, setText, and searchResults from 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 (
    <>
      

Using custom hook for debouncing

setText(e.target.value)} />

You entered: {value}

); }; export default App;

  • Import the hook created earlier.
  • Pass a function along with the timer to the useDebounce hook. This hook returns text, setText, and searchResults which are destructured.
  • When we type something into the input field, onChange is fired where we pass e.target.value to setText.

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 !!!

Salman Inayat Email 𝕏 GitHub