Debouncing in React – How to Delay a JS Function

In software development, certain tasks can be resource-intensive. For example, an API that searches a list of users, and we can’t afford to call it too often. We want to ensure that the search is only initiated once the entire search query has been entered.

This is where debouncing comes in – it’s a technique used in software development to regulate the frequency at which heavy tasks like this API are executed.


Problem

Let’s check an example of code implemented without debouncing and how it is harmful to our web app.

App.js
import React, { useEffect, useState } from "react";

const App = () => {
  const [searchQuery, setSearchQuery] = useState("");
  const [suggestions, setSuggestions] = useState([]);

  useEffect(() => {
    getSuggestions(searchQuery);
  }, [searchQuery]);

  const getSuggestions = (value) => {
    fetch(`https://demo.dataverse.org/api/search?q=${value}`)
      .then((res) => res.json())
      .then((json) => setSuggestions(json.data.items));
  };

  return (
    <div style={{ textAlign: "center" }}>
      <h2>Debouncing in React JS</h2>
      <div style={{ width: "40%", margin: "auto" }}>
        <input
          type="text"
          className="search"
          placeholder="Search here...."
          onChange={(e) => setSearchQuery(e.target.value)}
          style={{ width: "100%", height: "25px", fontSize: "20px" }}
        />

        {suggestions.length > 0 && (
          <div className="autocomplete">
            {suggestions.map((el, i) => (
              <div key={i} style={{ textAlign: "left" }}>
                <span>{el.name}</span>
              </div>
            ))}
          </div>
        )}
      </div>
    </div>
  );
};

export default App;

We used the onChange event with a text box to get the current value of the text box, and we stored this value in the state searchQuery.

We passed the state searchQuery as a dependency to useEffect hooks. So, when our state searchQuery changes, the useEffect runs. And our useEffect hook contains the getSuggestions() function where we call an API.

In short, each character type in the search box will trigger an API call.

If we check the network tab in the browser, we can see for each letter we typed in the search box, an API call was made.

How to Implement Debouncing in React JS
API calls without debouncing

This is harmful for the performance of our web app beacuse if the user types 50 or 1000 characters, it will make 50 or 100 API calls. This amount of API requests is not good for the server as well.

Let’s fix it using debouncing.


How to Implement Debouncing in React?

We will implement debouncing on an input box with an onChange event. So when we type in the search box for each keypress, an API call will be made.

We can implement debouncing in React JS in multiple ways.

  1. Using cleanup function
  2. Using lodash

1. Using cleanup function

In this method, we will use the useEffect cleanup function.

What is cleanup function?

In useEffect Hook, we can return a function, which returns a function where cleanup happens. The cleanup function removes unnecessary and unwanted behaviors and prevents memory leaks.

The cleanup function runs when the component unmounts and before executing the next scheduled effect.

Let’s perform debouncing on the previous example.

App.js
import React, { useEffect, useState } from "react";

const App = () => {
  const [searchQuery, setSearchQuery] = useState("");
  const [suggestions, setSuggestions] = useState([]);

  useEffect(() => {
    let timeoutId = setTimeout(() => {
      if (searchQuery) {
        getSuggestions(searchQuery);
      } else {
        setSuggestions([]);
      }
    }, 500);

    return () => {
      clearTimeout(timeoutId);
    };
  }, [searchQuery]);

  const getSuggestions = (value) => {
    fetch(`https://demo.dataverse.org/api/search?q=${value}`)
      .then((res) => res.json())
      .then((json) => setSuggestions(json.data.items));
  };

  return (
    <div style={{ textAlign: "center" }}>
      <h2>Debouncing in React JS</h2>
      <div style={{ width: "40%", margin: "auto" }}>
        <input
          type="text"
          className="search"
          placeholder="Search here...."
          onChange={(e) => setSearchQuery(e.target.value)}
          style={{ width: "100%", height: "25px", fontSize: "20px" }}
        />

        {suggestions.length > 0 && (
          <div className="autocomplete">
            {suggestions.map((el, i) => (
              <div key={i} style={{ textAlign: "left" }}>
                <span>{el.name}</span>
              </div>
            ))}
          </div>
        )}
      </div>
    </div>
  );
};

export default App;

In the above example, we used the cleanup function to perform debouncing.

Let’s understand it step by step.

1. Create timeout function

We created a timeout function in the useEffect hook; the timeout function has an interval of 500 milliseconds. That means it will trigger after 500 milliseconds.

The setTimeout() returns ID as a positive integer value that identifies the timer created by the call to setTimeout(). This value can be passed to clearTimeout() to cancel the timeout.

But we have an issue because we are using the state searchQuery as a dependency in our useEffect hook. So what it does is it will create a timeout function for each character we typed in, and each will be executed after 500 ms from when its created.

Somehow we need to remove these unwanted timeout events. Fortunately, JavaScript has the built-in method clearTimeout function to cancel timeouts. Another issue is when we need to remove the timeout call. For that, we can use the cleanup function, which helps us to remove the previous timeout on a new timeout call.

So, let’s create a cleanup function that cancels previous timeouts.

2. Return cleanup function

We returned the cleanup function that contains code for cancelling the last timeout event.

Let’s check network tab.

Debouncing in React JS
Debouncing in React JS

As you can see, just one API call is made after using debouncing.

Alternatively, you can use the loadash npm package to perform debouncing.


2. Using lodash

Lodash provides a debounce method to limit the execution rate of a particular code block. Using this method, we can limit our API calls.

Let’s see how can we do it:

App.js
import React, { useState } from "react";
import { debounce } from "lodash";

const App = () => {
  const [suggestions, setSuggestions] = useState([]);

  const getSuggestions = debounce((value) => {
    fetch(`https://demo.dataverse.org/api/search?q=${value}`)
      .then((res) => res.json())
      .then((json) => setSuggestions(json.data.items));
  }, 500);

  return (
    <div style={{ textAlign: "center" }}>
      <h2>Debouncing in React JS</h2>
      <div style={{ width: "40%", margin: "auto" }}>
        <input
          type="text"
          className="search"
          placeholder="Search here...."
          onChange={(e) => getSuggestions(e.target.value)}
          style={{ width: "100%", height: "25px", fontSize: "20px" }}
        />

        {suggestions.length > 0 && (
          <div className="autocomplete">
            {suggestions.map((el, i) => (
              <div key={i} style={{ textAlign: "left" }}>
                <span>{el.name}</span>
              </div>
            ))}
          </div>
        )}
      </div>
    </div>
  );
};

export default App;

Here we called API in lodash’s debounce function and set an interval of 500 ms. And called this function on the onChange event of the text box.


Conclusion

Now that you understand the concept and purpose of the debounce function, you can see how simple it is to use.

For example, when we enter a search query into an input field, the results will only be displayed 500 milliseconds after we’ve stopped typing – all thanks to debouncing.

Debouncing has many practical applications. It can be used to prevent excessive API calls and to ensure that form data is only submitted once.


Learn More:

Unlock your programming potential with Stack Thrive! Discover step-by-step tutorials, insightful tips, and expert advice that will sharpen your coding skills and broaden your knowledge.

Leave a Comment

Facebook Twitter WhatsApp