Debouncing and Throttling in React

Debouncing and Throttling in React

A detailed explanation of how to implement debouncing and throttling in react.

Table of contents

The reason for writing this blog is that I was asked this question in my frontend interview question and I could not explain to confidently of these concepts. So I hope after reading this you can understand and implement debouncing in React. Lets start then and there is a surprise visualization link added at the end of blog.

Debouncing

The bookish definition goes like this: Debouncing is a programming pattern or a technique to restrict the calling of a time-consuming function frequently, by delaying the execution of the function until a specified time to avoid unnecessary CPU cycles, and API calls and improve performance.

In simple words, debouncing will group together the series of sequential calls to a function into a single call to a function after a specified delay. It means that a function is called at the end of multiple events.

import { useState } from "react";
import "./styles.css";
import { useDebouncedValue } from "./hooks/useDebouncedValue";

export default function App() {
  const [search, setSearch] = useState(""); //input search state

  const debouncedSearch = useDebouncedValue(search, 1000); //custom hook with searched input and delay passed as parameters.

  const handleSearch = (e) => { //setting the search state with use input text onChange event.
    setSearch(e.target.value);
  };

  return (
    <div className="App">
      <label htmlFor="search">Search: </label>
      <input id="search" type="text" value={search} onChange={handleSearch} /> 
      <p>{debouncedSearch}</p>
    </div>
  );
}

In the above code we are taking a example of input search field. When users enters the product to search the text will be displayed when he takes a pause of 1000ms in between his typing. This means that as user types the function invocation is delayed by 1000ms and invoked when user takes a pause of 1000ms.

import { useEffect, useState } from "react";

export const useDebouncedValue = (value, delay) => {
  const [debouncedValue, setDebouncedValue] = useState(""); //debounced input state

  useEffect(() => { //useeffect which run of first render and when dependency array value changes.
    const id = setTimeout(() => { //setting the value of debounced input after a delay
      setDebouncedValue(value);
    }, delay);

    return () => clearTimeout(id); //clearing the timeout
  }, [value, delay]); //dependency array of value and delay

  return debouncedValue; //returning the debounced input
};

In the above code we are setting the debounced value of to input value after given delay and returning the debounced value at the end.

GitHub repo and Live link to show the difference between normal search and debounced search.

Throttling

Throttling limits the number of times the function can be called over a certain period. If user invokes the function in between the period then throttling prohibits it from invocation.

import { useState } from "react";
import "./styles.css";
import { useThrottleClicks } from "./hooks/useThrottleClicks";

export default function App() {
  const [count, setCount] = useState(0); //count state to show the number of time a function is invoked.

  const handleClick = () => { //increasing the count by 1
    setCount((count) => count + 1);
  };

  const handleThrottledClicks = useThrottleClicks(handleClick, 1000); // function invoked with callback handleClick and 1000ms delay.

  return (
    <div className="App">
      <button onClick={handleThrottledClicks}>Throttle Click</button>
      <p>{count}</p>
    </div>
  );
}

In the above code when user clicks on Throttle button multiple times the useThrottleClicks custom hook is invoked with callback and delay.

import { useCallback, useState } from "react";

export const useThrottleClicks = (cb, delay) => {
  const [isThrottled, setIsThrottled] = useState(false); //setting isThrottled to false

  const handleClick = useCallback(() => {
    if (!isThrottled) {
      cb(); //cb invoked which will increase the count by 1
      setIsThrottled(true);
      setTimeout(() => {
        setIsThrottled(false); //after a delay isThrottled set to false
      }, delay);
    }
  }, [cb, delay, isThrottled]); //dependancy array with cb, delay and isThrottled

  return handleClick; // return the handleClick
};

In the above code we invoke a callback function which increments the value of count by 1 after a delay. Note that even if user invokes the function repeatedly we prevent it by changing the value of isThrottled state in setTimeout.

GitHub repo and Live link to show the difference between throttle clicks and normal clicks.

As I was doing my research for debouncing and throttling I found this amazing visualization link to understand debouncing and throttling. As promised here it is: Throttle and Debounce Visualization.

I hope this blogs helps you understand debouncing and throttling. Hit like and comment if there are any suggestions.