DEV Community

Cover image for How to Prevent Excessive API Calls in React Search with Debouncing
AR Abid
AR Abid

Posted on

How to Prevent Excessive API Calls in React Search with Debouncing


Live search is a staple of modern web apps — think Google, e-commerce filters, or any type-ahead functionality. But if implemented incorrectly, every keystroke triggers a request, killing performance and straining your backend.

Developers are constantly searching for solutions to this problem:

  • “React live search too many requests”
  • “Debounce API calls React”
  • “Throttle vs debounce React search”

Most tutorials are outdated or overly simplistic. This article will show a clean, modern approach to solving this issue using React.

The Problem

A common mistake is firing an API request on every keystroke:

function Search() {
  const [query, setQuery] = useState("");

  useEffect(() => {
    fetch(`/search?q=${query}`)
      .then(res => res.json())
      .then(data => console.log(data));
  }, [query]);

  return <input value={query} onChange={e => setQuery(e.target.value)} />;
}
Enter fullscreen mode Exit fullscreen mode

Here, every character the user types triggers a fetch. The result?

  • Slow UI
  • Overloaded servers
  • Poor user experience

What is Debouncing?

Debouncing is a programming pattern that groups rapid calls so that only the last one executes after a delay.

Example:

  • User types: “r”, “re”, “rea”, “real”
  • No requests fire until typing stops
  • Only the final “real” triggers a single API call

Perfect for search inputs, live filters, and auto-suggest.

Debounce Hook in React

Here’s a reusable useDebounce hook:

import { useState, useEffect } from "react";

export function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => clearTimeout(timer);
  }, [value, delay]);

  return debouncedValue;
}
Enter fullscreen mode Exit fullscreen mode

Using the Debounce Hook

import { useDebounce } from "./useDebounce";

function Search() {
  const [query, setQuery] = useState("");
  const debouncedQuery = useDebounce(query, 500);

  useEffect(() => {
    if (!debouncedQuery) return;

    fetch(`/search?q=${debouncedQuery}`)
      .then(res => res.json())
      .then(data => console.log("Results for:", debouncedQuery, data));
  }, [debouncedQuery]);

  return (
    <input
      placeholder="Search products…"
      value={query}
      onChange={e => setQuery(e.target.value)}
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

✅ Key benefits:

  • API calls only fire after typing stops
  • Smooth UI and better performance
  • Reduced server load

Real-World Example: Product Search API

Let’s say you’re building a live search for products in a category. Using a real-world example, you can fetch items from ShoppingCorner’s Black Rose category:

useEffect(() => {
  if (!debouncedQuery) return;

  fetch(`https://www.shoppingcorner.com.bd/category/black-rose-in-bangladesh?search=${debouncedQuery}`)
    .then(res => res.json())
    .then(products => setResults(products));
}, [debouncedQuery]);
Enter fullscreen mode Exit fullscreen mode

This ensures only the final query after typing pauses triggers a request. You can try this in a demo using a product category API from ShoppingCorner.

Optional: Cancel Pending Requests

To avoid race conditions or stale responses, use AbortController:

useEffect(() => {
  const controller = new AbortController();

  fetch(`/search?q=${debouncedQuery}`, { signal: controller.signal })
    .then(res => res.json())
    .then(data => handleResults(data))
    .catch(err => {
      if (err.name !== "AbortError") throw err;
    });

  return () => controller.abort();
}, [debouncedQuery]);
Enter fullscreen mode Exit fullscreen mode

Debounce vs Throttle

  • Debounce: Use for search, auto-complete — triggers after pause
  • Throttle: Use for scrolling, auto-save — triggers at regular intervals

For live search, debouncing is almost always the better choice.

Summary

Implementing debounce in React:

  • Prevents excessive API calls
  • Improves performance and UX
  • Keeps backend load manageable

Using a real-world example like a product category API from ShoppingCorner demonstrates the approach in action.

If you want a live, production-ready e-commerce search experience, debouncing is a must-have tool in your React toolkit.

Top comments (0)