Redux Action and Action Creators & Redux Async Actions
Redux Action and Action Creators & Redux Async Actions

In this video you will learn everything that you need to know about actions, action types, action creators and async actions.
Let's jump right into it.

So now we must clarify everything about actions. So what is action? It's just an object with type property which is a string and any additional fields.
Here inside our addUser function this object that we returning is an action.

{ type: "ADD_USER", payload: username };

The next important thing is action creator. It's a function which returns the action. Why we need it at all? It helps us to pass data inside our actions and decouple our actions from the component. Previously we created it directly in component but actually it makes more sense to store them separately as we can use them in any component.

Normally we create additional store folder where we store everything which is related to Redux and an actions folder where we put all action creators that we have. If we don't have a lot of them we can just create actions.js file

export const addUser = (username) => {
  return { type: "ADD_USER", payload: username };
};

Now we can import it and use in our component

import { addUser } from "./store/actions";

As you can see everything is working as expected.


Now let's talk about action types. This is the term of the name of each action. So inside type we have some random string. But action it's a good approach to store all types in single place and reuse them everywhere. Let's create an actionTypes.js file for that.

store/actionTypes.js

const actionTypes = {
  addUser: "ADD_USER",
};

export default actionTypes;

store/actions.js

import actionTypes from "./actionTypes";

export const addUser = (username) => {
  return { type: actionTypes.addUser, payload: username };
};

We can also now use this actionType inside our reducer. And it also makes sense to move our reducer to store folder.

store/reducers.js

import actionTypes from "./actionTypes";

const reducer = (state = [], action) => {
  if (action.type === actionTypes.addUser) {
    return [...state, action.payload];
  }

  return state;
};

export default reducer;

Now let's talk about async actions or actually async action creators. So sometimes we want to do some asynchronous things like getting data from API for example. The problem is here that it doesn't fit in Redux at all. Our dispatch is just a function and our actions creators must return just an action. So we can't really write some fetch inside because it must directly return an action.

However there are several ways to make it working. And we will talk about most popular of them. We can allow to return promises from action creators with additional middleware redux-thunk. We already used middleware redux-logger in previous video so I you missed that I will link it down. Just to remind you middleware adds additional functionality to dispatch a function.

So normally we are dispatching actions but redux-thunk middleware upgrades our dispatch and allows to dispatch actions. Let's try this out.

First of all we must install redux-thunk middleware. But here is important point. order of middlewares is important and redux-logger must be the last middleware.

Secondly we must provide this middle to our applyMiddleware function.

yarn add redux-thunk
import thunkMiddleware from "redux-thunk";

const store = createStore(
  reducer,
  applyMiddleware(thunkMiddleware)
);

Now let's change our addUser action creator and return here not an action but a function.

export const addUser = (username) => {
  return (dispatch) => {
    dispatch({ type: actionTypes.addUser, payload: username });
  };
};

So here we are returning a function. And what is super important for us is that as an argument we are getting dispatch from Redux. Which means we can do in this function whatever we want and call dispatch as many times as possible.

As you can see in browser, everything is working as previously. So let's go again what is happening here. We wrote in connect addUser function and actually previously that meant that mapDispatchToProps just dispatches an action which was created when we called a function with some argument.

Now we have exactly the same but mapDispatchToProps dispatches a function. The only difference that dispatch works with functions because of the middleware and we can dispatch any amount of actions inside async actions.


But of course mostly people are using async actions to fetch data from API. So let's create an API and save our user there!

For this we can use json-server which allows us to create an API in a matter of seconds.

npm i -g json-server

Now let's create a db.json inside the root of our project. It will be our database

{
  "users": []
}

Now let's start our API

json-server --watch db.json --port=3004

Our API is there and we can try to save users inside our addUser method in our database.

export const addUser = (username) => {
  return async (dispatch) => {
    dispatch({ type: actionTypes.addUser, payload: username });
    const response = await fetch("http://localhost:3004/users", {
      method: "POST",
      body: JSON.stringify({
        username,
      }),
      headers: {
        "Content-Type": "application/json",
      },
    });
    const data = await response.json();
    dispatch({ type: actionTypes.addUserSuccess, payload: data });
  };
};

So here we made an API call to save our user. We also made a function that we return async. This is why we are allowed to use await. As you can see we are calling dispatch twice. One at the beginning and one after successful getting the data. In this case we have 2 Redux action and we can react both on start and on success. For example show loading on start and save data in reducer on success.

So this was everything that you need to know about actions, action types, action creator and asynchronous action.

Also if you want to improve your programming skill I have lots of full courses regarding different web technologies.

📚 Source code of what we've done