Debounce, Performance and React 
Tags: react, performance, javascript, tips
Published: 2020-11-06 02:43:39

# Debounce, Performance and React 

While "debounce" is a more broad software development pattern, this article will focus on debounce implemented in [React](https://reactjs.org/). 

## What is Debounce? 

Debounce is a way of _delaying some peice of code, until a specified time_ to avoid unnecessary CPU cycles and increase software performance. 

## Why does it matter? 

Performance.

Debounce allows us to increase application performance by limit the frequency of "expensive operations". 

Specifically, operations that require significant resources (CPU, Memory, Disk) to execute. "Expensive operations" or  slow application load times, cause freezes and delays in the user-interface, and require more of your network than is ultimately necessary.  

### Understanding through example 

Debounce makes the most sense in context. 

Imagine we have a simple movie search application: 

```js
import React, { useState } from "react";
import Axios from "axios"; // to simplify HTTP request 
import "./App.css";

/**
 * Root Application Component
 */
export default function App() {
  // store/update search text & api request results in state
  const [search, setSearch] = useState("");
  const [results, setResults] = useState([]);

  /**
   * Event handler for clicking search
   * @param {event} e
   */
  const handleSearch = async (e) => {
    e.preventDefault(); // no refresh
    try {
      const searchResults = await searchAny(search);
      await setResults(searchResults.Search);
    } catch (error) {
      alert(error);
    }
  };

  return (
    <div className="app">
      <header>React Movies</header>
      <main>
        <Search value={search} setValue={setSearch} onSearch={handleSearch} />
        <Movies searchResults={results} />
      </main>
    </div>
  );
}

/**
 * Movie Card component
 * @param {{movie}} props with movie object containing movie data
 */
function MovieCard({ movie }) {
  return (
    <div className="movieCard">
      <h4>{movie.Title}</h4>
      <img alt={movie.Title} src={movie.Poster || "#"} />
    </div>
  );
}

/**
 * Container to hold all the movies
 * @param {searchResults} param0
 */
function Movies({ searchResults }) {
  return (
    <div className="movies">
      {searchResults !== undefined && searchResults !== []
        ? searchResults.map((m) => <MovieCard key={m.imdbID} movie={m} />)
        : null}
    </div>
  );
}


/**
 * Search bar
 * @param {{string, function, function}} props
 */
function Search({ value, setValue, onSearch }) {
  return (
    <div className="search">
      <input
        type="text"
        placeholder="Movie name..."
        value={value}
        onChange={(e) => setValue(e.currentTarget.value)}
      />
      <button onClick={onSearch}>Search</button>
    </div>
  );
}

```

In the sample React application outlined above, HTTP request (the "expensive operation") containing the search string (title of the movie) to the [OMDb API](http://www.omdbapi.com/) are made when the user clicks the "Search" button. The API responds with a list of movies in JSON. 

> Note: The example above does NOT implement the Debounce pattern.

## Not Debounce 

Since the "expensive operation" in the example React application above **only** executes the HTTP request (i.e. "searches for movies") when the "Search" button within the `<Search />` component is clicked - Debouncing would have little or no effect on the application's performance. 

But that's not how most people use modern web applications. 

We are used to web-apps responding immediately as we enter text with our search results (e.g. [google](www.google.com)). So what happens if we refactor the code to work in that fashion? 

## Dynamically Searching 

Well the most straight-forward approach would be to listen to the `onChange` event for the `<Search />` component, and re-execute the HTTP request (the search) every-time the text changes.  

That means that if you were to search "Terminator", the `onChange` event would get called for each character in the string. Assuming it was typed with no typos, this would create at least 9 `get` HTTP requests: 

1. "t"
2. "te" 
3. "ter"
4. "term"
5. "termi"
6. "termina" 
7. "terminat" 
8. "terminato"
9. "terminator" 

That's 9 or more HTTP requests that may be re-executed so rapidly that the first request has not been responded to - not to mention processed, and rendered - before the next request is made. 

### Expensive Operations 

HTTP request are referred to as "expensive" operations because they involve creating a request, encoding the request, transmitting the request over the web, an API receiving the request, then the process repeats in reverse as the request is processed by the API and returned to the source (our React application). 

To make things worse, in our example, each HTTP response must be processed and mapped to components (`<Movies />` and `<MovieCard />`) to display the movie information. 

Since each `<MovieCard />` component has an image of the movie, each of this card's will then have to create another HTTP request to another resource to retrieve the image. 

Alternatively, we could keep execution of the search as it was originally, only initiating the `get` request, when the `<Search />` component's click event is triggered. 

Problem solved? 

Sure, for this simple example - however what happens when you add filtering:

Every movie returned from the [OMDb API](http://www.omdbapi.com/) has `Poster`,`Title`,`Type`,`Year`, and `imdbID` properties. Realistically, we might want to filter the returned results by `Year`, or `Type`.  

For simplicity, let's just explore filtering by `Year`. 

We can create a `<YearFilter />` component that will take in the search results as a prop, and then we can use a `.reduce()` function to get all the years of the movies being rendered: 

```js
  // use `reduce()` to get all the different years
  const years = searchResults.reduce((acc, movie) => {
    if(!acc.includes(movie.Year)) {
      acc = [...acc, movie.Year] 
    }
    return acc 
  },[]);
```

Next we would need create a select, and map all the different years in `<option>` elements within that `<select>`.


```js
// map the different years to
{<select> 
{years.map((year) => {
  return <option>{year}</option>
}}) 
}
```

Combine these two functions, and we should have a `<YearFilter>` component that displays the years of the movies returned by the search. 

It might look something like: 

```js
// imports 
import React from 'react' 

/**
 * Component for filtering the movies 
 * @param {{searchResults}} props 
 */
export const YearFilter = ({ searchResults }) =>  {

  // no filter if 
  if(searchResults && searchResults.length < 1) return null

  // get all the different years
  const years = searchResults.reduce((acc, movie) => {
    if(!acc.includes(movie.Year)) {
      acc = [...acc, movie.Year] 
    }
    return acc 
  },[]);


  // map the different years to
  const options = years.map((year) => {
    return <option>{year}</option>;
  });

  // return JSX 
  return (
    <div className="yearFilter">
      <label>Year</label>
      <select>{options}</select>
    </div>
    )  
}

export default YearFilter
```

Next we would monitor for the `<select>`'s `onChange` event, and filter out all the displayed movies to only those that match the result. 

I hope at this point you're getting the idea. To keep this article from turning into a tutorial, I'll pause on the example. 

The Problem we are solving is that we have a scenario in which we our React application has an expensive operation that is being re-executed rapidly, so rapidly that operation ("effect") may not even finish its execution before another call to the function "effect" is called. 

## Introducing Debounce 

With Debounce, we tell React to only re-execute the query after a certain amount of time. The simplest way to implement this would be to leverage the native `setTimeout()` function provided by JavaScript, and wrap the timeout around the "expensive operation". 


So let's focus just on the operation we are concerned about: retrieving movie titles. Logically, we may want to wait to make the request until someone has stopped typing, or once all the filters have been selected.

Since the [OMDb API](http://www.omdbapi.com/)'s free tier only allows 1,000 request per day, we also may want to limit how many requests are made for that reason as well. 

So here I've simplified the expensive operation we want to Debounce inside a `useEffect` hook:  

```js
useEffect(() => {
  // using Axios for simplicity 
  Axios.get(baseUrl, { params: {
    apiKey: 'YOUR-API-KEY', s: searchTitle
  } }).then(response => setResults(response.Search))
}, [searchTitle])
```

Now let's wrap our effect with a `setTimeout()` ensuring that the effect will only re-execute after a delay.  

```js 
useEffect(() => {
  // capture the timeout 
  const timeout = setTimeout(() => {
    Axios.get(baseUrl, { params: {
      apiKey: 'YOUR-API-KEY', 
      s: searchTitle 
      } }).then(response => setResults(response.Search))
  }, 400) // timeout of 250 milliseconds 

  // clear the timeout 
  return () => clearTimeout(timeout)
}, [searchTitle])
``` 

The `setTimeout()` function wrapped around the HTTP request to our API in this example now ensures that no matter how many times the effect is called (i.e. anytime the `searchTitle` changes), the actual network request cannot be called more frequently than in intervals of `400` milliseconds.  

> The time is an arbitrary number, adjust as needed 

## Keeping it "DRY" 

In all most real-world React applications, there isn't just a single network request. Well, "copy and paste" is never a good option in software development. If we simply copied the effect above and changed the function wrapped inside, we make the first programming mistake of repeating ourselves, and assume technical debt that could be problematic later. 

Rather than "copy and pasting" and modifying to suit unique needs, we can abstract the behavior. 

In React, we can abstract this functionality using a custom hook. 

```js
// useDebounce.js 
import { useEffect, useCallback } from 'react' 

export const useDebounce(effect, dependencies, delay) => {
  // store the provided effect in a `useCallback` hook to avoid 
  // having the callback function execute on each render 
  const callback = useCallback(effect, dependencies)

  // wrap our callback function in a `setTimeout` function 
  // and clear the tim out when completed 
  useEffect(() => {
    const timeout = setTimeout(callback, delay)
    return () => clearTimeout(timeout)
  }, 
  // re-execute  the effect if the delay or callback changes
  [callback, delay]
  )  
}

export default useDebounce 
```

Now anywhere there's an expensive operation that has potential to be executed often and/rapidly, we simply wrap that function ("effect") within the custom `useDebounce` hook: 

```js
useDebounce(() => {
  // effect 
  Axios.get(baseUrl, { params: {
      apiKey: 'YOUR-API-KEY', 
      s: searchTitle 
      } }).then(response => setResults(response.Search))
}, [searchTitle], 400)  // [dependencies, delay]
```

And that's Debounce, and how you can abstract the behavior of Debounce to re-use that logic (in a maintainable way) throughout your application. 


## Conclusion 

Implementing debounce in react applications can help avoid unnecessary operations and increase performance. By increasing performance, our React application becomes faster, more responsive to user-input, and provides an improved user-experience. 

This pattern can even be abstracted to a custom hook so that the pattern is easy to implement throughout your application, but will be _most_ impactful to "expensive operations" or "effects" that are frequently or rapidly re-executed (and it is not necessary to re-execute).  

What do you think? Does Debounce make sense to you? Will you use it?