Using the Redux Thunk to Dispatch Async Actions with React
Tags: react, redux, javascript, webdev
Published: 2021-01-25 01:49:02

**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 use React Redux and Redux Thunk to dispatch async actions in a React app.

### Installation

Redux Thunk is available with the `redux-thunk` package. To install it, we run:

```
npm install redux-thunk
```

### Usage

We can use it by using the `applyMiddleware` function to apply the `thunk` middleware from `redux-thunk` .

For example, we can use it as follows:

```
import React, { useEffect } from "react";
import ReactDOM from "react-dom";
import { Provider, useSelector, useDispatch } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";

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

const store = createStore(joke, applyMiddleware(thunk));

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

function App() {
  const joke = useSelector(state => state.value && state.value.joke);
  const dispatch = useDispatch();

  useEffect(() => {
    dispatch(fetchJoke());
  }, []);
  return <div className="App">{joke}</div>;
}

const rootElement = document.getElementById("root");
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  rootElement
);
```

In the code above, we have the `fetchJoke` function to dispatch an action into our store to get the joke and put the value in the store.

To dispatch async actions into our store, we have to apply the `thunk` middleware by writing:

```
const store = createStore(joke, applyMiddleware(thunk));
```

to apply the middleware.

Then in `App` , we call `dispatch` with the function returned from the `fetchJoke` passed inside. `fetchJoke` is an async action, which is also known as a thunk.

This will let us set the value from the API to the store properly.

### Composing Actions

We can call another action within an action. Therefore, we can compose them by calling another action that we defined as follows:

```
import React, { useEffect } from "react";
import ReactDOM from "react-dom";
import { Provider, useSelector, useDispatch } from "react-redux";
import { createStore, applyMiddleware, combineReducers } from "redux";
import thunk from "redux-thunk";

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

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

const reducers = combineReducers({
  joke,
  jokeById
});
const store = createStore(reducers, applyMiddleware(thunk));

const fetchJokeById = id => {
  return async dispatch => {
    const response = await fetch(`[https://api.icndb.com/jokes/${id](https://api.icndb.com/jokes/$%7Bid)}
    `);
    const joke = await response.json();
    dispatch({ type: "SET_JOKE_BY_ID", joke });
  };
};

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

function App() {
  const joke = useSelector(state => state.joke.value && state.joke.value.joke);
  const jokeById = useSelector(
    state => state.jokeById.value && state.jokeById.value.joke
  );
  const dispatch = useDispatch();

  useEffect(() => {
    dispatch(fetchJoke());
  }, []);
  return (
    <div className="App">
      <p>{joke}</p>
      <p>{jokeById}</p>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  rootElement
);
```

In the code above, we have 2 reducers.

In `App`, we called `fetchJoke` by dispatching the function returned by `fetchJoke` to the store in the `useEffect` callback. In `fetchJoke` , we ran:

```
dispatch({ type: "SET_JOKE", joke });
```

to set the joke in the `joke` reducer and then also called:

```
dispatch(fetchJokeById(1));
```

which calls the action function returned by `fetchJokeById` . This populates the joke in the `jokeById` store.

![](https://cdn-images-1.medium.com/max/800/0*yfwO5lWvNj4fM7vS)Photo by [Lauren Fleischmann](https://unsplash.com/@theburbgirl?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)

### Injecting a Custom Argument

Since Redux Thunk 2.1.0, we can use the `withExtraArgument` function to inject extra arguments to the function that we return in the thunk.

For example, we can use that method as follows:

```
import React, { useEffect } from "react";
import ReactDOM from "react-dom";
import { Provider, useSelector, useDispatch } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
function joke(state = {}, action) {
  switch (action.type) {
    case "SET_JOKE":
      return { ...action.joke };
    default:
      return state;
  }
}

const api = "https://api.icndb.com/jokes/random/";
const store = createStore(joke, applyMiddleware(thunk.withExtraArgument(api)));
const fetchJoke = () => {
  return async (dispatch, getState, api) => {
    const response = await fetch(api);
    const joke = await response.json();
    console.log(joke);
    dispatch({ type: "SET_JOKE", joke });
  };
};

function App() {
  const joke = useSelector(state => state.value && state.value.joke);
  const dispatch = useDispatch();
  useEffect(() => {
    dispatch(fetchJoke());
  }, []);
  return <div className="App">{joke}</div>;
}

const rootElement = document.getElementById("root");
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  rootElement
);
```

In the code above, we create the store by writing:

```
const store = createStore(joke, applyMiddleware(thunk.withExtraArgument(api)));
```

Then we have the following `fetchJoke` thunk:

```
const fetchJoke = () => {
  return async (dispatch, getState, api) => {
    const response = await fetch(api);
    const joke = await response.json();
    console.log(joke);
    dispatch({ type: "SET_JOKE", joke });
  };
};
```

which returns a function with 3 parameters. The third parameter is the argument that we passed in, which is `api` .

### Conclusion

Using Redux Thunk, we can pass in any function that calls `dispatch` into the `dispatch` function to manipulate our Redux store state.

We apply the thunk middleware, then we can dispatch thunks.

This means that we can call `dispatch` with other thunks within a thunk, so we can compose them easily.

Since Redux Thunk 2.1.0, we can use the `withExtraArgument` function to add an extra argument to the action that we return the function that we want to use with `dispatch`.