January 29, 2019

Asynchronous Operations in React-Redux

by Camilo Reyes

Asynchronous Operations in React-Redux

Asynchronous operations are a difficult challenge while programming the browser.

It is hard to conceptualize sending a request then waiting for a response without blocking. Browsers can put the request behind a callback and continue code execution.

The React-Redux libraries do much of the work for you without compromising simplicity. Think of React as the library that renders UI components in plain HTML. Redux is the state management library with asynchronous capabilities.

In the old days, you could delegate asynchronous behavior through chained callbacks. This had limitations because a large callback chain ran the risk of callback hell. A strong sense of discipline was necessary to keep the code as clean as possible. Callback hell could turn any project into mush when you lacked SOLID principles.

But, with React-Redux, what sort of improvements are available in async programming? For this, we’ll do a demo on the planets in the solar system. The goal is to load them on a page asynchronously with images. We’ll focus on Redux’s state management and how it handles asynchronous behavior. We’ll assume you have a working knowledge of npm and ES6. To keep the code samples focused, we’re only showcasing relevant code. If you’re interested in more of the plumbing, feel free to check out the entire demo on GitHub.

The dependencies for this demo will be as follows; if you’re following along, type:

npm install --save next react react-dom prop-types axios redux react-redux redux-thunk redux-logger

Make sure there is a package.json available to save all dependencies. Feel free to explore what each dependency will do. The two to keep in mind are axios to make async requests and redux-thunk to manage async state.

To start, capture constants we will use throughout the demo. These constants will drive the state of async operations:

export const REQUEST_PLANETS = 'request planets';
export const RECEIVE_PLANETS = 'receive planets';

The request gets triggered by the component’s componentDidMount method which then fires fetchPlanets. To help you visualize the result, here is the working demo:

The Planets

Why Not Use Ajax?

You can think of Redux-Thunk as another way to do Ajax in the browser. A thunk describes a function that replaces a computation you then calculate later. Execution does not happen immediately but gets delegated.

For example:

const foo = (i) => i + 1;

In JavaScript, you may think of a thunk as a callback function. A callback function delegates computation so it can execute later. In Redux-Thunk, you still make use of this same callback concept but in an abstract way. Redux’s statement management system handles all the implementation details. This eliminates the risk of long callback chains and callback hell.

Time to wire up Redux-Thunk in the Redux-Store:

const logger = createLogger();

const planetStore = createStore(
  reducers,
  applyMiddleware(logger, thunk)
);

With this, you’re all set for some asynchronous programming. Thunk now sets up action dispatchers so it can handle a thunk callback function. The Redux-Store is where you set up Redux-Thunk as middleware pipeline. Note we take this opportunity to include the logger, so we can see Redux-Thunk in action. The logger captures dispatcher messages and prints them in the browser’s console.

Protect your React App with Jscrambler

Async Actions with Axios

Next, let’s set up the actions that get dispatched through the store. In Redux, actions are like messages that ripple across the state management system. You can think of any UI interaction as an action. This fits well when you imagine firing and receiving messages to manage state. The UI notes a state change, fires an action, and Redux handles state changes through messages.

The PlanetAction is as follows:

let PlanetAction = {
  fetchPlanets() {
    return (dispatch) => {
      dispatch({ type: REQUEST_PLANETS });

      axios.get('/static/solar-system-planets.json')
        .then((response) => dispatch({
          type: RECEIVE_PLANETS,
          success: true,
          planets: response.data.planets
        }))

        .catch((error) => dispatch({
          type: RECEIVE_PLANETS,
          error: error.message,
          planets: null
        }));
    };
  }
};

Note the use of axios to encapsulate Ajax requests in the browser through a promise. The .then() function handles a successful response. The .catch() function handles an error. React-Thunk makes it possible for you to use a thunk as the return type. The underlying state management system handles the dispatcher. Note the use of a thunk with return (dispatch) => { }.

To handle errors gracefully, we opted to capture the error.message in the dispatcher’s message. This helps us debug the app because this message shows up in the console. The .catch() function enables error handling through the dispatcher callback function.

This is what the messages look like in the console:

Sad path React-Redux

React Reducers

The reducer picks up messages and returns the current state through a pure function. A pure function is one where given an input you always get back the exact same result. This paradigm is functional programming and it reduces unpredictable behavior in your programs.

Below is the reducer that changes state during asynchronous operations:

const INITIAL_STATE = [];

const planets = (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case REQUEST_PLANETS:
      return INITIAL_STATE;

    case RECEIVE_PLANETS:
      return action.planets;

    default:
      return state;
  }
};

We can reuse INITIAL_STATE because the app has the same data at page load and during the request. Note the result will always match a given input one-to-one. Reducers use a pure function and return state given where you are in the async operation. The action dispatcher then uses this pure function to calculate the current state. Note this reducer is the callback function inside the thunk function.

Now, below is the happy path with console messages:

Happy path React-Redux

Conclusion

Redux-Thunk has an asynchronous approach that wraps around a thunk function. This embraces the best of programming, such as delegates and pure functions.

If you’re familiar with legacy Ajax through callbacks, then a thunk is not very different. React-Redux grabs the best of what you know about vanilla JavaScript and wraps it for easier use. This library doesn’t reinvent the wheel, but automates sound programming through abstractions.

Last, but not least, if you're building React applications with sensitive logic, be sure to protect them against code theft and reverse-engineering by following our guide.