Debouncing with Redux Middleware
Tags: redux, javascript, react, programming
Published: 2020-07-06 12:48:19

---
title: Debouncing with Redux Middleware
published: true
description: Let's implement debouncing functionality using Redux middleware!
tags: redux, javascript, react, programming
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/wtvt0gbicy05rscjz69s.png
canonical_url: https://typeofnan.dev/debouncing-with-redux-middleware/
---

*Please give this post a 💓, 🦄, or 🔖 if it you enjoy it!*

<hr />

A common conundrum in today's front-end framework world is knowing when and how to take certain asynchronous actions, such as persisting data to a backend. If we're using a state management library like Redux, we might be further confused as to where without our Redux code we might put this logic.

<hr />

I make other easy-to-digest tutorial content! Please consider:

- Subscribing to my [DevTuts mailing list](https://buttondown.email/devtuts)
- Subscribing to my [DevTuts YouTube channel](https://youtube.com/c/devtutsco)

<hr />

## A Concrete Scenario

For the purposes of this blog post, let's assume we are using React with Redux and want to periodically save our state data to a backend. We have elected to use _debouncing_ to do this, meaning we'd like to perform the save action after our state hasn't changed for a certain amount of time.

## Considering Our Options

So, what are our options when using React with Redux? I think the following list covers it:

- **Do it in a component** - Have a component that subscribes to our state and, when it renders, do the debouncing/saving.
- **Do it in a redux action creator** - Using something like _thunk_ middleware, trigger the debounce function in an action create prior to dispatching the associated action.
- **Do it in a reducer** - As you update your site data in the reducer, call a debounce function. (See note below for why I think this option is bad).
- **Do it in Redux middleware** - Create a middleware that runs the debounce function anytime your state changes.

Note: I think all of these are actually legitimate ways **except** performing the save in a reducer. Reducers really should be pure functions and performing data fetching from within the reducer is a side effect.

## Why I Like the Middleware Approach

As I mentioned above, I think most of these approaches could work fine, but I especially like the middleware approach. It nicely isolates your saving code, can selectively define which actions cause saving to start, doesn't require installing thunk middleware if you're not already using it, and doesn't require you to include a component that exists only to handle saving.

## The Implementation

First, we can create a `saveDebounce` function that will be called by our middleware. To implement debouncing, we'll make use of `setTimeout` and `clearTimeout`.

```javascript
let saveTimer;
let debounceTime = 10000; // 10 seconds

const saveDebounce = data => {
  if (saveTimer) {
    clearTimeout(saveTimer);
  }

  saveTimer = setTimeout(() => {
    // Use request library of choice here
    fetch('my-api-endpoint', {
      method: 'POST',
      body: JSON.stringify(data),
    });
  }, debounceTime);
};
```

Next, the actual middleware, which is pretty simple.

```javascript
export const dataSaver = store => next => action => {
  saveDebounce(store.getState());
  return next(action);
};
```

As a user is modifying state, the `saveDebounce` function will clear any previous timeout and start a new one. Only when the user hasn't changed state for 10 seconds will our `fetch` actually be called.

Finally, we need to register our middleware with Redux. This is done when we create our `store`.

```javascript
import { createStore, combineReducers, applyMiddleware } from 'redux';
import { dataSaver } from '../middleware/dataSaver';

const allReducers = combineReducers(reducers);
const store = createStore(allReducers, applyMiddleware(dataSaver));
```

## Some Optimizations

The above code should get you started pretty well, but we can make some optimizations.

### Let's stop calling getState so much

Calling `getState` on our `store` every time is unnecessarily and potentially expensive. Let's only do that when we're actually performing our `fetch`.

```javascript
let saveTimer;
let debounceTime = 10000;

const saveDebounce = store => {
  if (saveTimer) {
    clearTimeout(saveTimer);
  }

  saveTimer = setTimeout(() => {
    fetch('my-api-endpoint', {
      method: 'POST',
      body: JSON.stringify(store.getState()),
    });
  }, debounceTime);
};

export const dataSaver = store => next => action => {
  saveDebounce(store);
  return next(action);
};
```

This of course means our `saveDebounce` function has to have knowledge of the store's `getState` method. I think this trade-off is worth the performance boost.

### Let's only save a piece of our state

It seems unlikely we would really want to save the entire state object to a backend. More likely, we would just want to save a piece of our state object, which only gets updated by one or more actions.

Let's pretend that we only want to save data when the `userDetails` part of state changes. Perhaps we know this happens only when the `UPDATE_USER_DETAILS` action is dispatched. Accordingly, we could make the following changes:

```javascript
let saveTimer;
let debounceTime = 10000;

const saveDebounce = store => {
  if (saveTimer) {
    clearTimeout(saveTimer);
  }

  saveTimer = setTimeout(() => {
    fetch('my-api-endpoint', {
      method: 'POST',
      body: JSON.stringify(store.getState().userDetails),
    });
  }, debounceTime);
};

export const dataSaver = store => next => action => {
  if (action.type === 'UPDATE_USER_DETAILS') {
    saveDebounce(store);
  }
  return next(action);
};
```

Now, we only consider firing the save event if the `UPDATE_USER_DETAILS` action is dispatched. Furthermore, other parts of the state can be updating without cancelling our debounce!

<hr />

*Please give this post a 💓, 🦄, or 🔖 if it you enjoy it!*