Introduction

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.

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.

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.

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.

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.

#redux

Debouncing with Redux Middleware
15.50 GEEK