How do you implement debouncing or throttling using Hooks?

Implementing Debouncing and Throttling with React Hooks

When working with events like search inputs, scrolling, or resizing, it's important to optimize performance by reducing unnecessary function calls. Two common techniques for this are:

  • Debouncing: Delays execution until after a certain period of inactivity.
  • Throttling: Ensures execution happens at most once in a given time interval.

1. Debouncing with useEffect and setTimeout

Debouncing is useful when handling search inputs or resizing where you want to execute a function only after the user stops typing/moving.

* Custom useDebounce Hook
import { useState, useEffect } from "react";

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

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

    return () => clearTimeout(handler); // Cleanup previous timer
  }, [value, delay]);

  return debouncedValue;
}
* Usage: Debounced Search Input
function SearchBar() {
  const [query, setQuery] = useState("");
  const debouncedQuery = useDebounce(query, 500); // Waits 500ms after last input

  useEffect(() => {
    if (debouncedQuery) {
      console.log("Fetching results for:", debouncedQuery);
    }
  }, [debouncedQuery]);

  return (
    <input
      type="text"
      value={query}
      onChange={(e) => setQuery(e.target.value)}
      placeholder="Search..."
    />
  );
}
* Key Benefits :
  • Prevents excessive API calls.
  • Updates only after user stops typing.

2. Throttling with useRef and setTimeout :

Throttling ensures a function executes at most once per interval. It's useful for scrolling, resizing, or button spamming.

* Custom useThrottle Hook
import { useState, useEffect, useRef } from "react";

function useThrottle(value, delay) {
  const [throttledValue, setThrottledValue] = useState(value);
  const lastExecuted = useRef(Date.now());

  useEffect(() => {
    const handler = setTimeout(() => {
      if (Date.now() - lastExecuted.current >= delay) {
        setThrottledValue(value);
        lastExecuted.current = Date.now();
      }
    }, delay - (Date.now() - lastExecuted.current));

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

  return throttledValue;
}
* Usage: Throttled Scroll Event
function ScrollLogger() {
  const [scrollPos, setScrollPos] = useState(0);
  const throttledScrollPos = useThrottle(scrollPos, 1000); // Throttles updates every 1s

  useEffect(() => {
    const handleScroll = () => {
      setScrollPos(window.scrollY);
    };

    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  }, []);

  return <p>Scroll Position: {throttledScrollPos}</p>;
}
* Key Benefits :
  • Limits execution rate to once per interval.
  • Improves performance in scroll-heavy applications.

When to Use Debounce vs Throttle?
Feature Debounce Throttle
Use Case Search input, API calls Scroll, resize, click events
Execution Fires after user stops Fires at fixed intervals
Example Auto-suggest search bar Tracking scroll position