Debouncing in React: Unleashing the Power of Efficient API Calls
Chirag Mittal
Posted on July 27, 2023
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]
);
Taking a basic example, Typing useState
in the search box would result in 8 total API calls, one after each keystroke.
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]
);
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.
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.
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.
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.
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!
Posted on July 27, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.