Mastering Debouncing: Beyond API Calls - A Journey Through Efficient Event Handling

Mastering Debouncing: Beyond API Calls - A Journey Through Efficient Event Handling

Ever wondered how your favorite websites make searches so smooth?

Debouncing: Simple Language

Let's keep things simple.

Example 1: Think of a 'cool down period' like after you type a letter, the computer takes a moment before it acts on it or do something right ? If you keep on typing and typing, it does waits until you pause and then do the thing. You got it! That what debouncing is actually. It's only after you stop typing that it does something.

Example 2: Imagine you're talking to someone and you only keep on saying. What the opposite one expects is to give a pause and let the other person act/react on what you said, then say again. That is what debouncing is and the pause that you gave may be one or two second for the other person to react, you can change timing of the network calls here also according to your wish.

Debouncing: Technical Language

Debouncing is a technique used to control how often a particular function gets called, especially in scenarios like search bars where there can be frequent input changes.

It ensures that a function is only called after a certain period of time has passed since the last time it was called.

This helps in optimizing performance by preventing unnecessary function calls, particularly in situations where frequent updates are not needed or may overload the system.

import { useEffect, useState } from "react"

export default function App() {
  const [filter, setFilter] = useState("")
  const [data, setData] = useState([])

  // Function to debounce calls
  let timeout;

  const debounceGetData = () =>{
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      getData();
    }, 1000); // Calls the getData function after 1000 milliseconds (1 second)
  }

  // Function to Fetch Data
  const getData = async () => {
    try {
      const response = await fetch("https://api.agify.io?name=" + filter);
      if (!response.ok) {
        throw new Error('Network response was not ok');
      }
      const data = await response.json();
      setData(data.age);
    } catch (error) {
      console.error('Error:', error);
      // Optional: You can handle the error your way
    }
  }

  // Use effect for triggering the dependency
  useEffect(()=>{
    debounceGetData(); // Calls the debounce function

    // Cleanup function for the next call
    return () => {
      clearTimeout(timeout);
    }
  },[filter])

  return (
    <main>
      <input onChange={e=>setFilter(e.target.value)} type="text"/>
      <div>Data: {data}</div>
    </main>
  )
}

Output

Okay, now let's break down the code and understand it deeply!

  1. State Variables

 const [filter, setFilter] = useState("")
 const [data, setData] = useState([])
  • filter stores the user's input in the search bar every time the input changes

  • data stores the fetched data from the API

  1. Debouncing Function : with 1000ms timer

let timeout;

const debounceGetData = () =>{
  clearTimeout(timeout);
  timeout = setTimeout(() => {
    getData();
  }, 1000);
}
  • debounceGetData() is a function that ensures getData() is not called too frequently.

  • It clears any existing timeouts and sets a new timeout of 1 second before calling getData().

  • This delays the API call until the user stops typing for at least 1 second or 1000 milisecond.

  1. Fetching Data: Function for API call

const getData = async () => {
  try {
    const response = await fetch("https://api.agify.io?name=" + filter);
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    const data = await response.json();
    setData(data.age);
  } catch (error) {
    console.error('Error:', error);
  }
}
  • getData() function fetches data from an external API (https://api.agify.io) based on the user's input stored in filter.

  • If the response is successful, it sets the fetched age data to the data state.

  • Error handling is included to log any errors that occur during the fetch operation.

  1. Purpose of useEffect Hook:

useEffect(()=>{
   //FUNCTION -> debounceGetData()
  debounceGetData();

  // CLEANUP FUNCTION -> return()=>{}
  return () => {
    clearTimeout(timeout);
  }
//dependency ->[filter]
},[filter])
  • It triggers debounceGetData() whenever filter state changes (i.e., when the user types in the search bar).

  • The cleanup function clears the timeout whenever filter state changes, ensuring only one API call is made after the user stops typing.

  1. Input Element: OnChange method

<input onChange={e=>setFilter(e.target.value)} type="text"/>
  • Renders an input element (search bar) where users can type their search queries.

  • onChange event handler updates the filter state when the user's input changes.

  1. Displaying Data: Output

<div>Data: {data}</div>
  • Displays the fetched data (data.age) below the search bar.

A General Case: Efficient API Calls

When a user types 'A' into the search box and then pauses for 1 second, here's what happens:

  1. Initially, the filter state variable is empty.

  2. When the user types 'A', the filter state variable updates with the value 'A'.

  3. Since the filter dependency changes, the useEffect hook is triggered.

  4. The useEffect hook calls the debounceGetData() function, which sets a timer for 1 second before making the API call.

  5. If the user continues typing within that 1-second window, the useEffect hook will be triggered again due to the change in the filter dependency.

  6. However, before setting up a new timer, the cleanup function in the useEffect hook runs (in the second call). This clears the previous timer to prevent multiple API calls.

  7. Then, the debounceGetData() function is called again, setting up a new timer for 1 second.

If the cleanup function wasn't there, the debounceGetData() function would start a new timer each time useEffect is triggered, resulting in multiple timers running simultaneously. This would lead to multiple API calls being made, which is not the desired behavior. Therefore, the cleanup function ensures that only one timer is active at a time, preventing unnecessary API calls and ensuring that the API call only occurs after a 1-second pause in user input.

Other Use Cases:

Debouncing is a versatile concept that extends beyond just API calls. While we've just explored its application in managing API calls and async requests in a React application, its application is used in various scenarios in programming. Let's delve deeper into how debouncing can be employed in different contexts, using everyday examples to illustrate its significance.

1. Button Clicks and Form Submissions:

Okay, so imagine you're using a website, like when you're filling out a form or clicking on buttons. Sometimes, when you click a button or submit a form, it can trigger things to happen in the background, like sending information to the website's server or doing some heavy work.

Now, here's the thing: sometimes, if you click too quickly or accidentally press the button multiple times, it might cause problems. It could send the same information more than once, which can mess things up or slow things down.

So, to avoid this mess, developers use something called "debouncing." It's like putting a little delay or pause between each click or form submission. This way, even if you click the button a bunch of times in a row, it only registers once. It's like telling the website, "Hey, chill out a bit, I only meant to click once!" This helps prevent mistakes and keeps things running smoothly.

2. Autocomplete and Suggestions:

Now, imagine you're typing something in a search bar, like on Google or when you're writing an email. You know how it sometimes gives you suggestions as you type? That's called autocomplete.

Now, normally, every time you type a letter, the website or app might ask the server, "Hey, do you have any suggestions for what the user might be typing?" But if it did that for every single letter you type, it would put a lot of strain on the server, especially if you're typing fast.

So, to make things smoother and less stressful for the server, they use something called debouncing. It's like giving a little break between each letter you type. Instead of asking for suggestions after every single keystroke, the system waits for a moment when you stop typing for a bit. Then, it asks for suggestions. This way, it reduces the number of times it bothers the server and makes sure you still get those helpful suggestions without slowing things down. This makes your typing experience smoother and saves energy for the server.

Conclusion:

So, now that you understand debouncing and feel confident with it, I hope you found this helpful! If you did, please share it on social media and follow me here.

It won't cost you anything but it will encourage me to write more about similar topics in the future.

I build amazing cool stuffs. Check out my GitHub

Did you find this article valuable?

Support Aniket Sinha by becoming a sponsor. Any amount is appreciated!