Dispatching Async Actions with Redux

John Au-Yeung - Jan 25 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

With Redux, we can use it to store data in a central location in our JavaScript app. It can work alone and it’s also a popular state management solution for React apps when combined with React-Redux.

In this article, we’ll look at how to dispatch actions asynchronously.

Async Actions

In JavaScript, a lot of things have to be done asynchronously to avoid blocking the main execution thread.

Redux can dispatch asynchronous actions so that we don’t always have to change states synchronously.

To do this, we use the Redux-Thunk middleware. We can apply middlewares by using Redux’s applyMiddleware function.

With that, we can pass in functions that call dispatch instead of plain objects. We need this capability to run async actions that call dispatch and populate the store with it.

We can do this as follows:

import { createStore, applyMiddleware } from "redux";
import thunkMiddleware from "redux-thunk";

function jokeReducer(state = {}, action) {
  switch (action.type) {
    case "SET_JOKE":
      return action.joke;
    default:
      return state;
  }
}

let store = createStore(jokeReducer, applyMiddleware(thunkMiddleware));

function fetchJoke() {
  return async dispatch => {
    const response = await fetch("[https://api.icndb.com/jokes/random](https://api.icndb.com/jokes/random)");
    const joke = await response.json();
    dispatch({ type: "SET_JOKE", joke });
  };
}

(async () => {
  await store.dispatch(fetchJoke());
  console.log(store.getState().value.joke);
})();
Enter fullscreen mode Exit fullscreen mode

In the code above, we have the jokeReducer which is the same as what we have before.

Then in to create the store, we write:

let store = createStore(jokeReducer, applyMiddleware(thunkMiddleware));
Enter fullscreen mode Exit fullscreen mode

The difference from stores that only take plain object actions is that we have the applyMiddleware function where we passed in the thunkMiddleware.

The thunkMiddleware lets us pass in functions to dispatch instead of a plain object without it.

In the function we pass in, we can call dispatch with plain objects to do the synchronous actions.

Then we defined the fetchJoke function, which is or action creator, as follows:

function fetchJoke() {
  return async dispatch => {
    const response = await fetch("[https://api.icndb.com/jokes/random](https://api.icndb.com/jokes/random)");
    const joke = await response.json();
    dispatch({ type: "SET_JOKE", joke });
  };
}
Enter fullscreen mode Exit fullscreen mode

In this function, we returned an async function that has the Redux dispatch function as the parameter.

Then once we get the joke with the Fetch API, we call dispatch provided in the parameter.

This way the Thunk middleware can call dispatch in the returned function.

We can return any function, but we need to use thunks for async functions since they don’t return plain objects.

Ordinary action creators must return plain objects, but if we use the Redux Thunk middleware, we can return a function that calls dispatch and getState.

Then we dispatch our async action and get the value from it as follows:

(async () => {
  await store.dispatch(fetchJoke());
  console.log(store.getState().value.joke);
})();
Enter fullscreen mode Exit fullscreen mode

All we did above was call dispatch with the promise that the fetchJoke function returned as we defined above. Then we use store.getState().value.joke to get the joke’s value from the store.

Photo by Sincerely Media on Unsplash

Checking State Before Dispatching Synchronous Actions in Our Async Action Creator

We can write a function to check the state before getting data. To do this, we can make the following changes while keeping everything else the same:

function shouldFetchJoke(state) {
  return !state.value || !state.value.joke;
}

function fetchJoke() {
  return async (dispatch, getState) => {
    if (shouldFetchJoke(getState())) {
      const response = await fetch("[https://api.icndb.com/jokes/random](https://api.icndb.com/jokes/random)");
      const joke = await response.json();
      dispatch({ type: "SET_JOKE", joke });
    }
  };
}
Enter fullscreen mode Exit fullscreen mode

In the code above, we defined the shouldFetchJoke function which checks the state.value and state.value.joke to see if the value has been set.

Then we change fetchJoke to call shouldFetchJoke . Then we take advantage of the getState parameter to get the state and check if a joke already exists in the store with shouldFetchJoke .

If not, then we proceed to get the joke.

Conclusion

With the Redux Thunk middleware, we can pass in actions that aren’t plain objects to dispatch.

To use it, we have to call applyMiddleware with the thunkMiddleware as the argument.

Then we can create an action creator that returns a function with the dispatch and getState functions as the first and second parameter respectively, then we can get the state with getState as usual.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .