DEV Community

Cover image for Debouncing in React: Unleashing the Power of Efficient API Calls
Chirag Mittal
Chirag Mittal

Posted on • Edited on

Debouncing in React: Unleashing the Power of Efficient API Calls

Hey there, fellow tech enthusiasts! Today, I want to take you on a journey through a problem I encountered while learning React - one that left me scratching my head and wondering how to improve performance and user experience.

Picture this: a project aimed at fetching a list of movies from a remote server while the user types in an input box. Seemed straightforward, right? Well, not quite.

As I delved deeper into the implementation, I stumbled into a pretty big issue, which lead to a flood of API requests, slower renders, and a waste of valuable resources. Though the tutorial I followed mentioned using AbortController, it failed to limit the storm of requests that were made.

Now, let me tell you something about myself - I am obsessed with Performance & Speed. Watching those numerous API requests go to waste wasn't something I could bear. I knew there had to be a better way. Three more weeks of learning React has brought me to this very moment - ready to share the wonders of debouncing with you all.

A Makeshift Solution

The project was configured in such a way that it would fire off a request to the server every time a keystroke happened.

useEffect(
    function () {
      async function fetchMovies() {
        try {
          const res = await fetch(
                `http://www.someapi.com/?apikey=${KEY}&s=${query}`);
        } catch (err) {
        }
      }
      fetchMovies();
    },
    [query]
  );
Enter fullscreen mode Exit fullscreen mode

Taking a basic example, Typing useState in the search box would result in 8 total API calls, one after each keystroke.

API Requests without debouncing

Now, this might have been useful for auto-search completion, but to me, it was just a waste of resources. Why make 8 requests when I just need 1? Keeping that in mind, I got to work.

With limited knowledge of React at hand and a fair share of trial and error, I was able to come up with a makeshift solution involving the use of setTimeout.

Here's what the code looked like:

useEffect(
    function () {
        const controller = new AbortController(); 
            // We still need the AbortController, 
            // will come back to it later

        const waitFetch = setTimeout(async function fetchMovies() {
            try {
                const res = await fetch(
                    `https://www.someapi.com??apikey=${KEY}&s=${query}`
                    ,{ signal: controller.signal }};
            // Doing things here
                } catch (err) {
            } 
        }, 300); //timeout delay

        return function () {
        controller.abort(); 
        clearTimeout(waitFetch); //Clearing the timeout
        };
    },
    [query]
);
Enter fullscreen mode Exit fullscreen mode

API Requests after Debouncing (using setTimeout)

This did the trick for me. Rather than making a request every keystroke, it was only being made once I stopped typing(after some idle time). It was only way later I discovered that this technique is called Debouncing. The proper implementation might be a bit different, but that's the crux of it.

In essence, we are limiting the number of requests. If we are going to discard them anyways, might as well not make them in the first place.

Why use Debouncing?

Now you might be wondering, why go through this much of a headache to save some API calls? I get that, and there are 2 good reasons for that.

  1. Saving User Data & Bandwidth: Less number of requests means that less amount of data will be transferred back & forth. This not only enhances the user experience but also frees up bandwidth for many important operations.

  2. Cost-Effective Server Management: For server administrators, fewer API requests means reduced server load & data bills. Debouncing helps prevent unnecessary strain on the server. (P.S. You have a lesser risk of going broke if you use something like AWS!)

Debouncing isn't just limited to a single use case of fetching data. Instead, it can also be applied to various scenarios throughout web development, such as Window Resize events, Scrolling animations, Real-time filtering etc.

Is AbortController even needed?

Looking at the above example, you might be thinking why I still left the abort controller in there, if it never even gets called. There's a pretty interesting reason for that. You see, the debouncing function still has an edge case we need to address.

Continuing the example from before, let's imagine the user starts typing again once the request has been made. A new request will be made after the 300ms delay. For some reason, let's assume the data from the new request ends up arriving earlier than the previous one.

API Requests without AbortController (data-racing)

This data-racing situation could lead to the data from the latest request being overwritten by the previous one, resulting in outdated or incorrect information.

To safeguard against this scenario, we hold onto the AbortController. Even if the previous request has been triggered, we can use the AbortController to terminate it, and ensure that only the most recent response is used.

API Requests with AbortController

Beyond Debouncing!

As we approach the end of this journey through the realm of debouncing, I hope you feel empowered to utilize this powerful technique in your web development endeavors.

It's not just about writing code, it's about crafting exceptional experiences for your users and optimizing the performance of your projects.

Do keep in mind that our journey doesn't end here. Debouncing is just the tip of the iceberg, and there's, even more, we can do to optimize our applications. For example, you can experiment with using useMemo with the debounce function, to make it more efficient. Additionally, for a simpler solution, the debounce function provided in lodash is a fantastic option.

Thank you for joining me on this adventure, and until we meet again, happy coding, fellow developers! I would love to hear your thoughts in the comments below.


If you enjoyed this blog post, consider giving me a follow. I'll be covering more topics related to performance optimization in the future. Also, if you have any questions, feel free to reach out to me on Twitter or LinkedIn anytime. I'm always ready to assist you.

On a personal note, I'm currently seeking a remote React developer job. If you have any leads for job openings or freelance opportunities, I would be extremely grateful for your support!

Let's continue pushing the boundaries of web development together and creating remarkable experiences for our users. The future holds endless possibilities, and I can't wait to explore them with you. Happy coding!

Top comments (10)

Collapse
 
efpage profile image
Eckehard

Pretty hard to understand, why this even could happen? Debouncing is a very common pattern on event based code, not only to prevent API flooding, but also to get a better user experience.

But it is hard to understand, why people use React and still get this type of problems? The whole Idea behind React was to prevent people from thinking about those details....

Collapse
 
kuvambhardwaj profile image
Kuvam Bhardwaj

React helps us think in terms of individual components, this is what the purpose of react is,

To abstract away low-level details like managing state, syncing state with UI and not writing html in js by hand every time for a new element.

Debouncing API calls is indeed a very common pattern but so are other common patterns, specific functions/libraries can be made internal or external.

Stop expecting everything from a library, it can only get you so far.

Collapse
 
efpage profile image
Eckehard

I was expecting this from React, as it hides many of the implementation details behind it´s virtual DOM. But maybe it does have some kind of debouncing, just in a different timescale. Waiting for a keypress might be much longer than the averate response time of React, isn´t it?

Thread Thread
 
kuvambhardwaj profile image
Kuvam Bhardwaj

No I don't think so buddy, it's just a library and libraries are supposed to abstract away only the complex bits, nothing more.

Thread Thread
 
chiragm999 profile image
Chirag Mittal

Yes, Exactly!

Collapse
 
chiragm999 profile image
Chirag Mittal

Absolutely, you're right! Debouncing is indeed a common pattern.

It's just that when I was working on the problem, I was still in the early stages of learning React, and my understanding of debouncing was limited. The goal was to share my thought process and how I tackled the challenge, hoping others could benefit from it. Your feedback is valuable, and I truly appreciate your perspective.

Collapse
 
efpage profile image
Eckehard • Edited

I´ve been coming relatively late to web programming, so, I´m still struggeling to understand, why people use React. Surely, React takes you the burden of managing state changes manually, but dues this really pay back? Managing state changes is also a common pattern on any UI system, so there are plenty of easy solutions for that. So, I expected that at least debouncing should not be an issue in React.

Usually, states do not change by themself, there is always a reason, why states are changing. Usually any kind of event. You can try to compare all states to the previous state to find, if anything has changed. Or you can use an event based approach, which is often much more surgical. Even React needs to do the DOM updates, finally.

If you use a simple timeout to prevent retriggering, this may indeed block your event handling completely, but it is no rocket science to care, that the ui is updated, if the event was blocked for more than 1 s or so.

Anyway, It´s intersting to see, that problems on that level are pretty much the same. Thank you for sharing!

Thread Thread
 
chiragm999 profile image
Chirag Mittal

While React does handle state changes efficiently through its virtual DOM, the issue with excessive API requests during live search remains. React triggers useEffect functions with every state update, including query changes, which can lead to unnecessary requests.

By using debouncing, we can optimize this process. The UI updates aren't blocked completely; they can happen outside the setTimeout, but we delay the actual search until the user stops typing. It's about providing a better user experience by limiting needless requests.

The only reason I think React is being used would be its component-based model and reusability. It allows us to write scalable and maintainable code. Though sometimes that may lead to a drop in performance.

Collapse
 
elijahtrillionz profile image
Elijah Trillionz

Well written and explained.
Thanks for sharing

Collapse
 
chiragm999 profile image
Chirag Mittal

Thank you for your kind words! I'm glad you found it helpful 😄