react redux tutorial for beginners

react redux tutorial for beginners

React redux tutorial for beginners

When I first started learning Redux I wish I had some “Redux for dummies”. I felt dumb because I couldn’t wrap my head around actions, action creators, reducers. And how about middleware? You’ve got to be kidding me!

So during those days I started teaching Redux for the sake of learning more, while writing my own React Redux tutorial. Since then I learned a lot and this React Redux tutorial for beginners is an effort for helping you too to learn Redux, in the most simplest way.

Enjoy the reading!

Table of Contents

React Redux tutorial for beginners: who this guide is for

The following React Redux guide is for JavaScript developers with a good grasp of ES6 and React. There are countless React redux tutorial for beginners online if you want to refresh your understandings, but if you’re just starting out I suggest reading Getting Started with React by Tania Rascia.

If you want to refresh your JavaScript skills before or after taking this tutorial check out my “Little JavaScript Book”, available for free on Github or as a PDF/ePub/Mobi.

React Redux tutorial for beginner: what you will learn

The guide covers mostly Redux with React because of the wide adoption of this combination but Redux can also be used as a stand-alone library without any frontend framework/library.

So in the following guide you will learn:

  • what is Redux
  • Redux’s building blocks
  • how to use Redux stand-alone
  • how to use Redux with React

React Redux tutorial: a minimal React development environment

Before starting off make sure you have a React development environment in place.

To make one you can follow How to set up React, webpack, and Babel or even better, use create-react-app:

npx create-react-app react-redux-tutorial

Once done you’re good to go.

React Redux tutorial for beginners: what is the state?

What is Redux? To answer that question we must first talk about state in JavaScript web applications. Consider a simple user flow:

“as a user I can click a button named Click me and a modal should appear soon after”.

Guess what, even in this trivial interaction there is a state we must deal with. For example, we can describe the initial state of the app as a plain JavaScript object:

const state = {
  buttonClicked: 'no',
  modalOpen: 'no'
}

When the user clicks, the state changes and we have:

const state = {
  buttonClicked: 'yes',
  modalOpen: 'yes'
}

How do you reliably keep track of these state changes? What if the state is mutated by accident by some unrelated piece of logic? Is there a library that can help us?

Also, if you worked with React before the term state should be no surprise to you. I guess you already wrote some “stateful” React component like this:

import React, { Component } from "react";

class ExampleComponent extends Component {
  constructor(props) {
    super(props);

    this.state = {
      articles: [
        { title: "React Redux Tutorial for Beginners", id: 1 },
        { title: "TypeScript tutorial for beginners", id: 2 }
      ]
    };
  }

  render() {
    const { articles } = this.state;
    return <ul>{articles.map(el => <li key={el.id}>{el.title}</li>)}</ul>;
  }
}

stateful React component is a JavaScript class (with React hooks that’s no longer the case).

In a React component the state holds data which can be rendered to the user. The state in React could also change in response to actions and events: in fact you can update the local component’s state with setState().

So, in general a typical JavaScript application is full of state. For example, state is:

  • what the user sees (data)
  • the data we fetch from an API
  • the URL
  • the items selected inside a page
  • eventual errors to show to the user

Having seen the basics let’s now talk about what problem Redux tries to solve.

What problem does Redux solve?

So, state is everywhere in web applications. From now on I’ll talk about state and logic in the context of a typical React application, but consider these concept applicable to any frontend architecture, regardless of the specific library. The thing is, can you imagine how much state a web application application has?

Even an innocent single page app could grow out of control without clear boundaries between every layer of the application. This holds particularly true in React.

Yeah, you can get by with keeping the state within a parent React component (or in context) as long as the application remains small. Then things will become tricky especially when you add more behaviours to the app. At some point you may want to reach for a consistent way to keep track of state changes. Not only, I’d say that frontend components shouldn’t know about the business logic. Ever.

Unfortunately a ton of logic gets stuffed into frontend components these days. Is there an alternative to this agony?

Redux can solve exactly those problems. It might not be clear in the beginning, but Redux helps giving each frontend component the exact piece of state it needs.

Even better, Redux can hold business logic inside its own layer (middleware), alongside with the code for fetching data. The benefits of this approach are manifold.

In the next sections we’ll see when and how to use Redux in your applications. But first a couple of words about the quintessential question: “should I use Redux”?

React Redux tutorial for beginners: should I use Redux?

I must admit I’m a bit biased here because I’m fond of Redux and I suggest it whenever I join a new project. But maybe you don’t need neither Redux, nor alternatives like Mobx and friendsEven more if you’re working on a simple project.

In React for example there are many ways to avoid using Redux: children props, the context API.

But for medium to bigger projects I always found Redux (or an equivalent) almost mandatory: state management libraries beautifully keep logic and behaviours abstracted away from the UIUI testability skyrockets and so developer productivity. But if you look at it from another perspective you may argue that Redux has a cost. It adds another layer of abstraction to your application.

What cost are you willing to pay? Convoluted frontend components or multiple layers of abstractions?

Enough talking. In the next section we’ll start building a proof of concept to introduce:

  • Redux fundamental principles
  • Redux alongside with React

Again, make sure you have a React development environment ready to use!

React Redux tutorial for beginners: getting to know the Redux store

When I approached Redux for the first time I was confused. There is too much terminology: action, reducer, middleware. But on top of that it wasn’t clear to me how were all the moving parts glued together? There were some minions or what? In Redux there are no minions (unfortunately).

What orchestrates all the cogs is the store. Repeat with me: the store. The store in Redux is kind of magic and holds all of the application’s state.

So let’s create a store to start playing with Redux. Move into your React development environment and install Redux:

cd react-redux-tutorial

npm i redux --save-dev

Create a directory for the store:

mkdir -p src/js/store

Next up create a new file, src/js/store/index.js and initialize the store:

// src/js/store/index.js

import { createStore } from "redux";
import rootReducer from "../reducers/index";

const store = createStore(rootReducer);

export default store;

As you can see, store is the result of calling createStore, a function from the Redux library. createStore takes a reducer as the first argument and in our case we passed in rootReducer (not yet present).

You may also pass an initial state to createStore, useful for server side rendering and state preloading, but for now we’re not interested in that. The most important concept to understand here is that the state in Redux comes from reducers. Let’s repeat: reducers produce the state of your application.

Armed with that knowledge let’s move on to our first Redux reducer.

See the branch on Github

React Redux tutorial for beginners: getting to know Redux reducers

What’s a reducer? A Redux reducer is just a JavaScript function. It takes two parameters: the current state and action (more about actions soon).

In a typical React component the local state might be mutated in place. In Redux you’re not allowed to do that. The third principle of Redux (as outlined by its creator) prescribes that the state is immutable and cannot change in place.

In other words the reducer must be pure. A pure function is one that returns the exact same output for the given input. But despite this terminology reasoning about a reducer is not that hard.

In our example we’ll be creating a simple reducer which takes initial state ad action as parameters. Create a directory for the root reducer:

mkdir -p src/js/reducers

Then create a new file, src/js/reducers/index.js:

const initialState = {
  articles: []
};

function rootReducer(state = initialState, action) {
  return state;
};

export default rootReducer;

Notice how the initial state is passed as a default parameter. But as of now our reducer does nothing than returning the initial state.

In the next section we’ll add an action to the mix, and things will become interesting.

See the branch on Github

React Redux tutorial for beginners: getting to know Redux actions and named constants

Redux reducers are without doubt the most important concept in Redux. Reducers produce the state of an application. But how does a reducer know when to generate the next state?.

The second principle of Redux says the only way to change the state is by sending a signal to the store. This signal is an action. So “dispatching an action” means sending out a signal to the store.

Confused? The reassuring thing is that Redux actions are nothing more than JavaScript objects. This is how an action looks like:

{
  type: 'ADD_ARTICLE',
  payload: { title: 'React Redux Tutorial', id: 1 }
}

As you can see it’s a JavaScript object with two properties: type and payload.

The type property drives how the state should change and it’s always required by Redux. The payload property instead describes what should change, and might be omitted if you don’t have new data to save in the store.

As a best practice in Redux we wrap every action within a function, so that object creation is abstracted away. Such function takes the name of action creator: let’s put everything together by creating a simple action creator.

Create a directory for actions:

mkdir -p src/js/actions

Then create a new file, src/js/actions/index.js:

// src/js/actions/index.js

export function addArticle(payload) {
  return { type: "ADD_ARTICLE", payload }
};

You can notice that the type property is a string. Strings are prone to typos and duplicates and for this reason it’s better to declare actions as constants. Create a new folder for them:

mkdir -p src/js/constants

Then create a new file, src/js/constants/action-types.js:

// src/js/constants/action-types.js

export const ADD_ARTICLE = "ADD_ARTICLE";

Now open up again src/js/actions/index.js and update the action to use action types:

// src/js/actions/index.js

import { ADD_ARTICLE } from "../constants/action-types";

export function addArticle(payload) {
  return { type: ADD_ARTICLE, payload };
}

As you can see we’re starting to deal with multiple files. That’s the main pet peeve with Redux for most people. You can follow the Redux duck convention if you want, it keeps every Redux related piece in a single file.

One step closer to have a working Redux application, but first we need to tweak the reducer for catching the new action.

See the branch on Github

Refactoring the reducer

We left last section with the following question: how does a reducer know when to generate the next state? The key here is the Redux store. When an action is dispatched, the store forwards a message (the action object) to the reducer.

At this point the reducer says “oh, let’s look at the type property of this action”. Then depending on the action type, the reducer produces the next state, eventually merging the action payload into the new state.

Earlier we created a reducer that does nothing. Let’s fix it! Open up src/js/reducers/index.js and update the reducer with an if statement for checking the action type:

// src/js/reducers/index.js

import { ADD_ARTICLE } from "../constants/action-types";

const initialState = {
  articles: []
};

function rootReducer(state = initialState, action) {
  if (action.type === ADD_ARTICLE) {
    state.articles.push(action.payload);
  }
  return state;
}

export default rootReducer;

There is also a line of code which pushes the action payload into the initial state. Seems the right thing to do. But it’s wrong! Our reducer breaks the main Redux principle: immutability.

Array.prototype.push is an impure function: it modifies the original array. But there’s more. We’re also changing the initial state in place.

We need a fix. First we can return a new state with Object.assign, that is, a new JavaScript object. This way we keep the original state unaltered. Then we can use Array.prototype.concat in place of Array.prototype.push for keeping the original array:

import { ADD_ARTICLE } from "../constants/action-types";

const initialState = {
  articles: []
};

function rootReducer(state = initialState, action) {
  if (action.type === ADD_ARTICLE) {
    return Object.assign({}, state, {
      articles: state.articles.concat(action.payload)
    });
  }
  return state;
}

export default rootReducer;

Now the initial state is left in pristine conditions and the resulting state is just a copy of the initial state. Remember two key points for avoiding mutations in Redux:

  • use concat(), slice(), or the spread operator for arrays
  • use Object.assign() or object spread of objects

If this stuff about immutability feels boilerplate to you, redux starter kit has solved the problem. Take a look at it once you finish this tutorial.

In the next section we’ll play with Redux from the browser’s console. Hold tight!

See the branch on Github

React Redux tutorial for beginners: Redux store methods

This will be super quick, I promise.

I want you to play with the brower’s console for gaining a quick understanding of how Redux works.

Redux itself is a small library (2KB). The Redux store exposes a simple API for managing the state. The most important methods are:

  • getState for accessing the current state of the application
  • dispatch for dispatching an action
  • subscribe for listening on state changes

We will play in the brower’s console with the above methods.

To do so we have to export as global variables the store and the action we created earlier.

Create a new file named src/js/index.js and update the file with the following code:

import store from "../js/store/index";
import { addArticle } from "../js/actions/index";

window.store = store;
window.addArticle = addArticle;

Now open up src/index.js as well, clean up its content and update it as follows:

import index from "./js/index"

Now run webpack dev server (or Parcel) with:

npm start

head over http://localhost:8080/ and open up the console with F12.

Since we’ve exported the store as a global variable we can access its methods. Give it a try!

Start off by accessing the current state:

store.getState()

output:

{articles: Array(0)}

Zero articles. In fact we haven’t update the initial state yet.

To make things interesting we can listen for state updates with subscribe.

The subscribe method accepts a callback that will fire whenever an action is dispatched. Dispatching an action means notifying the store that we want to change the state.

Register the callback with:

store.subscribe(() => console.log('Look ma, Redux!!'))

To change the state in Redux we need to dispatch an action. To dispatch an action you have to call the dispatch method.

We have one action at our disposal: addArticle for adding a new item to the state.

Let’s dispatch the action with:

store.dispatch( addArticle({ title: 'React Redux Tutorial for Beginners', id: 1 }) )

Right after running the above code you should see:

Look ma, Redux!!

To verify that the state changed run again:

store.getState()

The output should be:

{articles: Array(1)}

And that’s it. This is Redux in its simplest form.

Was that difficult?

Take your time to explore these three Redux methods as an exercise. Play with them from the console:

  • getState for accessing the current state of the application
  • dispatch for dispatching an action
  • subscribe for listening on state changes

That’s everything you need to know for getting started with Redux.

Once you feel confident head over the next section. We’ll go straight to connecting React with Redux!

React Redux tutorial for beginners: connecting React with Redux

After learning Redux I realized it wasn’t so complex. I knew how to access the current state with getState. I knew how to dispatch an action with dispatch and how to listen for state changes with subscribe.

Yet I didn’t know how to couple React and Redux together.

I was asking myself: should I call getState within a React component? How do I dispatch an action from a React component? And so on.

Redux on its own is framework agnostic. You can use it with vanilla Javascript. Or with Angular. Or with React. There are bindings for joining together Redux with your favorite framework/library.

For React there is react-redux.

Before moving forward install react-redux by running:

npm i react-redux --save-dev

To demonstrate how React and Redux work together we’ll build a super simple application. The application is made of the following components:

  • an App component
  • a List component for displaying articles
  • a Form component for adding new articles

(The application is a toy and it does nothing serious other than displaying a list and a form for adding new items. Nonetheless it’s still a good starting point for learning Redux)

React Redux tutorial for beginners: what is react-redux?

react-redux is a Redux binding for React. It’s a small library for connecting Redux and React in an efficient way.

The most important method you’ll work with is connect.

What does react-redux’s connect do? Unsurprisingly it connects a React component with the Redux store.

You will use connect with two or three arguments depending on the use case. The fundamental things to know are:

  • the mapStateToProps function
  • the mapDispatchToProps function

What does mapStateToProps do in react-redux? mapStateToProps does exactly what its name suggests: it connects a part of the Redux state to the props of a React component. By doing so a connected React component will have access to the exact part of the store it needs.

And what about mapDispatchToProps? mapDispatchToProps does something similar, but for actions. mapDispatchToProps connects Redux actions to React props. This way a connected React component will be able to dispatch actions.

Is everything clear? If not, stop and take your time to re-read the guide. I know it’s a lot to learn and it requires time. Don’t worry if you don’t get Redux right know. It will click sooner or later.

In the next section we’ll finally get our hands dirty!

React Redux tutorial for beginners: App component and Redux store

We saw that mapStateToProps connects a portion of the Redux state to the props of a React component. You may wonder: is this enough for connecting Redux with React? No, it’s not.

To start off connecting Redux with React we’re going to use Provider.

Provider is an high order component coming from react-redux.

Using layman’s terms, Provider wraps up your React application and makes it aware of the entire Redux’s store.

Why so? We saw that in Redux the store manages everything. React must talk to the store for accessing the state and dispatching actions.

Enough theory.

Open up src/js/index.js, wipe out everything and update the file with the following code (if you’re in create-react-app modify src/index.js instead):

import React from "react";
import { render } from "react-dom";
import { Provider } from "react-redux";
import store from "./store/index";
import App from "./components/App.jsx";
// if you're in create-react-app import the files as:
// import store from "./js/store/index";
// import App from "./js/components/App.jsx";

render(
  <Provider store={store}>
    <App />
  </Provider>,
  // The target element might be either root or app,
  // depending on your development environment
  // document.getElementById("app")
  document.getElementById("root")
);

You see? Provider wraps up your entire React application. Moreover it gets the store as a prop.

Now let’s create the App component since we’re requiring it. It’s nothing special: App should import a List component and render itself.

Create a directory for holding the components:

mkdir -p src/js/components

and a new file named App.jsx inside src/js/components:

// src/js/components/App.jsx
import React from "react";
import List from "./List";

const App = () => (
  <div className="row mt-5">
    <div className="col-md-4 offset-md-1">
    <h2>Articles</h2>
      <List />
    </div>
  </div>
);

export default App;

Save and close the file, then move on to creating List.

React Redux tutorial for beginners: List component and Redux state

We have done nothing special so far.

But our new component, List, will interact with the Redux store.

A brief recap: the key for connecting a React component with Redux is connect.

Connect takes at least one argument.

Since we want List to get a list of articles it’s a matter of connecting state.articles with the component. How? With mapStateToProps.

Create a new file named List.jsx inside src/js/components. It should look like the following:

// src/js/components/List.jsx

import React from "react";
import { connect } from "react-redux";

const mapStateToProps = state => {
  return { articles: state.articles };
};

const ConnectedList = ({ articles }) => (
  <ul className="list-group list-group-flush">
    {articles.map(el => (
      <li className="list-group-item" key={el.id}>
        {el.title}
      </li>
    ))}
  </ul>
);

const List = connect(mapStateToProps)(ConnectedList);

export default List;

The List component receives the prop articles which is a copy of the articles array we saw in the Redux state. It comes from the reducer:

const initialState = {
  articles: []
};

function rootReducer(state = initialState, action) {
  if (action.type === ADD_ARTICLE) {
    return Object.assign({}, state, {
      articles: state.articles.concat(action.payload)
    });
  }
  return state;
}

Always remeber: the state in redux comes from reducers. Now,it’s a matter of using the prop inside JSX for generating a list of articles:

{articles.map(el => (
  <li className="list-group-item" key={el.id}>
    {el.title}
  </li>
))}

React protip: take the habit of validating props with PropTypes or even better, use TypeScript

Finally the component gets exported as List. List is the result of connecting the stateless component ConnectedList with the Redux store.

Still confused? I was too. Understanding how connect works will take some time. Fear not, the road to learn Redux is paved with “ah-ha” moments.

I suggest taking a break for exploring both connect and mapStateToProps.

Once you’re confident about them head over the next section!

React Redux tutorial for beginners: Form component and Redux actions

The Form component we’re going to create is a bit more complex than List. It’s a form for adding new items to our application.

Plus it is a stateful component.

A stateful component in React is a component carrying its own local state.

A stateful component? “Valentino, we’re talking about Redux for managing the state! Why on earth would you give Form its own local state??”

Even when using Redux it is totally fine to have stateful components.

Not every piece of the application’s state should go inside Redux.

In this example I don’t want any other component to be aware of the Form local state.

The form component contains some logic for updating the local state upon a form submission.

It receives a Redux action as well. This way it can update the global state by dispatching the addArticle action.

Create a new file named Form.jsx inside src/js/components. It should look like the following:

// src/js/components/Form.jsx
import React, { Component } from "react";
import { connect } from "react-redux";
import uuidv1 from "uuid";
import { addArticle } from "../actions/index";

function mapDispatchToProps(dispatch) {
  return {
    addArticle: article => dispatch(addArticle(article))
  };
}

class ConnectedForm extends Component {
  constructor() {
    super();

    this.state = {
      title: ""
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({ [event.target.id]: event.target.value });
  }

  handleSubmit(event) {
    event.preventDefault();
    const { title } = this.state;
    const id = uuidv1();
    this.props.addArticle({ title, id });
    this.setState({ title: "" });
  }

  render() {
    const { title } = this.state;
    return (
      <form onSubmit={this.handleSubmit}>
        <div className="form-group">
          <label htmlFor="title">Title</label>
          <input
            type="text"
            className="form-control"
            id="title"
            value={title}
            onChange={this.handleChange}
          />
        </div>
        <button type="submit" className="btn btn-success btn-lg">
          SAVE
        </button>
      </form>
    );
  }
}

const Form = connect(null, mapDispatchToProps)(ConnectedForm);

export default Form;

What can I say about the component? Besides mapDispatchToProps and connect it’s standard React stuff.

mapDispatchToProps connects Redux actions to React props. This way a connected component is able to dispatch actions.

You can see how the action gets dispatched in the handleSubmit method:

// ...
  handleSubmit(event) {
    event.preventDefault();
    const { title } = this.state;
    const id = uuidv1();
    this.props.addArticle({ title, id }); // Relevant Redux part!!
// ...
  }
// ...

Finally the component gets exported as Form. Form is the result of connecting ConnectedForm with the Redux store.

Side note: the first argument for connect must be null when mapStateToProps is absent like in the Form example. Otherwise you’ll get TypeError: dispatch is not a function.

Our components are all set!

Update App to include the Form component:

import React from "react";
import List from "./List.jsx";
import Form from "./Form.jsx";

const App = () => (
  <div className="row mt-5">
    <div className="col-md-4 offset-md-1">
      <h2>Articles</h2>
      <List />
    </div>
    <div className="col-md-4 offset-md-1">
      <h2>Add a new article</h2>
      <Form />
    </div>
  </div>
);

export default App;

Install uuid with:

npm i uuid --save-dev

Now run webpack (or Parcel) with:

npm start

and head over to http://localhost:8080

You should see the following working POC:

React Redux tutorial demo. Nothing fancy but still useful for showing React and Redux at work

Nothing fancy but still useful for showing React and Redux at work!

The List component on the left is connected to the Redux store. It will re-render whenever you add a new item.

React Redux demo

Psst .. If you don’t see anything on the browser make sure that document.getElementById(“app”) in src/js/index.js matches a real element inside the page:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" >
    <title>How to set up React, Webpack, and Babel</title>
</head>

<body>
    <div class="container">
        <div id="root">
        </div>
    </div>
</body>

</html>

(Don’t forget to include Bootstrap too). Whoaaa! You did it! But we’re not done yet! In the next section we’ll look at Redux middlewares. Hold tight!

React Redux tutorial for beginners: what is a Redux middleware?

So far we saw the building blocks of Redux: the store, in charge for orchestrating all the interactions in Redux. Then we saw the reducer which is a producer: reducers make the state in Redux.

Then there are actions, plain JavaScript objects with a property named type. Finally we have action creators which are plain JavaScript function in charge for returning Redux actions.

On top of that we saw some of the Redux’s principle: the state is immutable and can change only in response to actions.

Now, imagine the following scenario: you want to prevent the user from creating articles containing particular words inside the title. Let’s take a look at handleSubmit in Form.jsx:

// ...
  handleSubmit(event) {
    event.preventDefault();
    const { title } = this.state;
    const id = uuidv1();
    this.props.addArticle({ title, id });
  }
// ...

We can just add a check before this.props.addArticle right? It could be something like:

// ...
  handleSubmit(event) {
    event.preventDefault();
    const { title } = this.state;
    const id = uuidv1();
    // let's add a check for forbidden words
    const forbiddenWords = ['spam', 'money'];
    const foundWord = forbiddenWords.filter(word => title.includes(word) )
    if (foundWord) {
      return this.props.titleForbidden();
    }
    //
    this.props.addArticle({ title, id });
  }
// ...

But wasn’t the entire point of Redux moving the logic out of our React components? Yes! So what? Can we check the title property inside the reducer? Maybe! And while we’re there let’s dispatch another action in response to a forbidden word. But how I’m supposed to access dispatch inside a reducer? Hold on …

It’s clear that we want to reach for something different. Looks like we want to check the action payload (and the title property) before the actions is passed to the reducer. There should be a way for tapping into the application’s flow and altering its behaviour. And guess what, that’s exactly what a Redux middleware does.

A Redux middleware is a function that is able to intercept, and act accordingly, our actions, before they reach the reducer. And while the theory is quite simple, a Redux middleware can look a bit confusing. In its basic form a Redux middleware is a function returning a function, which takes next as a parameter. Then the inner function returns another function which takes action as a parameter and finally returns next(action).

Are you still there? Hold tight because you’re going to write your first Redux middleware. Here’s how it looks like:

function forbiddenWordsMiddleware() {
  return function(next){
    return function(action){
      // do your stuff
      return next(action);
    }
  }
}

I know, you want to cry and change career but bear with me. Middlewares in Redux are super important because they will hold the bulk of your application’s logic. If you think about it there is no better place than a middleware for abstracting away business logic. And the nice thing is that while inside the middleware you can access getState and dispatch, like so:

function forbiddenWordsMiddleware({ getState, dispatch }) {
  return function(next){
    return function(action){
      // do your stuff
      return next(action);
    }
  }
}

Armed with that knowledge we can create our first Redux middleware: it should check whether the action’s payload has bad words into it! We’ll see the actual implementation into the next section.

React Redux tutorial for beginners: your first Redux middleware

The middleware we’re going to build should inspect the action’s payload.

There are a lot of benefits from using a Redux middleware, even for simplest tasks:

  • the logic can live outside React (or any other library/framework)
  • middlewares become reusable pieces of logic, easily to reason about
  • middlewares can be tested in isolation
  • we keep the components clean

So, let’s get our hands dirty! Create a new folder for holding middlewares:

mkdir -p src/js/middleware

Now create a new file named index.js in src/js/middleware. The structure of our first middleware should match the following:

function forbiddenWordsMiddleware({ dispatch }) {
  return function(next){
    return function(action){
      // do your stuff
      return next(action);
    }
  }
}

For now we don’t need getState, we just get dispatch as the first parameter. Nice. Let’s implement the logic now. We need to check the action payload, namely the title property. If the title matches one or more bad words we stop the user from adding the article.

Also, the check should fire up only when the action is of type ADD_ARTICLE. It makes sense. How about this one?

import { ADD_ARTICLE } from "../constants/action-types";

const forbiddenWords = ["spam", "money"];

export function forbiddenWordsMiddleware({ dispatch }) {
  return function(next) {
    return function(action) {
      // do your stuff
      if (action.type === ADD_ARTICLE) {
        
        const foundWord = forbiddenWords.filter(word =>
          action.payload.title.includes(word)
        );

        if (foundWord.length) {
          return dispatch({ type: "FOUND_BAD_WORD" });
        }
      }
      return next(action);
    };
  };
}

Here’s what the middleware does: when action type is ADD_ARTICLE check if action.payload.title contains a bad word. If it does then dispatch an action of type “FOUND_BAD_WORD”, otherwise let the next action pass.

And this last point is really important: you should always return next(action) in your middlewares. If you forget to return next(action) the application will stop, and no other action will reach the reducer.

Now, time to wire up forbiddenWordsMiddleware to the Redux store. For that we need to import our middleware, another utility from Redux (applyMiddleware) and then cook everything together.

Open up  src/js/store/index.js and modify the file like so:

// src/js/store/index.js

import { createStore, applyMiddleware } from "redux";
import rootReducer from "../reducers/index";
import { forbiddenWordsMiddleware } from "../middleware";

const store = createStore(
  rootReducer,
  applyMiddleware(forbiddenWordsMiddleware)
);

export default store;

Oh, and for using Redux Dev Tools together with other middlewares here’s what you should do (notice the use of compose):

// src/js/store/index.js

import { createStore, applyMiddleware, compose } from "redux";
import rootReducer from "../reducers/index";
import { forbiddenWordsMiddleware } from "../middleware";

const storeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

const store = createStore(
  rootReducer,
  storeEnhancers(applyMiddleware(forbiddenWordsMiddleware))
);

export default store;

Save and close the file, run npm start and check if the middleware works. Try to add an article with “money” in its title:

React Redux tutorial: your first Redux middleware

Indeed it works! Good job! Now, here’s an exercise for you: update the reducer so it recognises the new action “FOUND_BAD_WORD”. Ideally it should update the state with a message of sort that can be showed to the user.

Then add a new action creator for “FOUND_BAD_WORD”. Update the middleware for using the action creator instead of the plain action. Try to add a test for your middleware. Think about it, you’ll find out it’s really simple to test middlewares in Redux.

And that’s it! In the next sections we’ll explore asynchronous actions in Redux with Redux Thunk and Redux Saga!

React Redux tutorial for beginners: asynchronous actions in Redux, the naive way

So far we were dealing with synchronous data. That is, the act of dispatching an action is synchronous. No AJAX, no promises. We return a plain object from our action creators. And when the action reaches the reducer we return the next state.

Now, suppose we want to fetch data from an API. In React you would put a call in componentDidMount and call it a day. But how about Redux? What’s a good place for calling asynchronous functions? Let’s think a moment about it.

Reducers? No no. Reducers should stay lean and clean. A reducer is not a good place for asynchronous logic.

Actions? How I am supposed to do that? Actions in Redux are plain objects. And what about action creators? An action creator is a function, and it looks like a nice spot for calling an API, doesn’t it? Let’s give it a shot.

We’ll create a new action named getData. This action calls an API with fetch and returns a Redux action.

Open up src/js/actions/index.js and create a new action named getData:

// src/js/actions/index.js

// ...
// our new action creator. Will it work?
export function getData() {
  return fetch("https://jsonplaceholder.typicode.com/posts")
    .then(response => response.json())
    .then(json => {
      return { type: "DATA_LOADED", payload: json };
    });
}

It makes perfect sense. But will it work? Now let’s wire up a React component so it dispatches getData from componentDidMount. We’ll use mapDispatchToProps (this time with the object shorthand form) for mapping Redux action creators to our component’s props. Create a new React component in src/js/components/Posts.jsx:

import React, { Component } from "react";
import { connect } from "react-redux";
import { getData } from "../actions/index";

export class Post extends Component {
  constructor() {
    super();
  }

  componentDidMount() {
    // calling the new action creator
    this.props.getData();
  }

  render() {
    return null;
  }
}

export default connect(
  null,
  { getData }
)(Post);

And finally update src/js/components/App.jsx to use the new component:

import React from "react";
import List from "./List.jsx";
import Form from "./Form.jsx";
import Post from "./Posts.jsx";

const App = () => (
  <div className="row mt-5">
    <div className="col-md-4 offset-md-1">
      <h2>Articles</h2>
      <List />
    </div>
    <div className="col-md-4 offset-md-1">
      <h2>Add a new article</h2>
      <Form />
    </div>
    <div className="col-md-4 offset-md-1">
      <h2>API posts</h2>
      <Post />
    </div>
  </div>
);
export default App;

Save and close the files, and look at what we’ve got in the browser:

React Redux tutorial: asynchronous actions in Redux with Redux Thunk

Interesting! “Error: Actions must be plain objects. Use custom middleware for async actions”. It looks like we cannot call fetch from within an action creator in Redux. Now what?

For making things work we need a custom middleware. Luckily there’s something ready for us: redux-thunk.

React Redux tutorial for beginners: asynchronous actions in Redux with Redux Thunk

We just learned that calling fetch from an action creator does not work. That’s because Redux is expecting objects as actions but we’re trying to return a Promise. With redux-thunk we can overcome the problem and return functions from action creators. Inside that function we can call APIs, delay the dispatch of an action, and so on.

First we need to install the middleware with:

npm i redux-thunk --save-dev

Now let’s load the middleware in src/js/store/index.js:

// src/js/store/index.js

import { createStore, applyMiddleware, compose } from "redux";
import rootReducer from "../reducers/index";
import { forbiddenWordsMiddleware } from "../middleware";
import thunk from "redux-thunk";

const storeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

const store = createStore(
  rootReducer,
  storeEnhancers(applyMiddleware(forbiddenWordsMiddleware, thunk))
);

export default store;

At this point we need to refactor getData to use redux-thunk. Open up src/js/actions/index.js and update the action creator like so:

// src/js/actions/index.js

// ...

export function getData() {
  return function(dispatch) {
    return fetch("https://jsonplaceholder.typicode.com/posts")
      .then(response => response.json())
      .then(json => {
        dispatch({ type: "DATA_LOADED", payload: json });
      });
  };
}

That’s redux-thunk!

A few things worth noting in the new version of getData: the fetch call gets returned from an outer function and the outer function has dispatch as a parameter. If you want to access the state inside the action creator you can add getState in the parameter’s list.

Also, notice the use of dispatch inside thenWe need to explicitly call dispatch inside the async function for dispatching the action.

With that in place we’re ready to update our reducer with the new action type. Open up src/js/reducers/index.js and add a new if statement. We can also add a new key inside initialState for saving the articles from the API:

// src/js/reducers/index.js
import { ADD_ARTICLE } from "../constants/action-types";

const initialState = {
  articles: [],
  remoteArticles: []
};

function rootReducer(state = initialState, action) {
  if (action.type === ADD_ARTICLE) {
    return Object.assign({}, state, {
      articles: state.articles.concat(action.payload)
    });
  }

  if (action.type === "DATA_LOADED") {
    return Object.assign({}, state, {
      remoteArticles: state.remoteArticles.concat(action.payload)
    });
  }
  return state;
}

export default rootReducer;

(I know, I didn’t put DATA_LOADED inside its own named costant. I’d left as an exercise for you. Hope you don’t mind!)

Finally we’re ready to update our Post component for displaying our “remote” posts. We will use mapStateToProps for selecting ten posts:

import React, { Component } from "react";
import { connect } from "react-redux";
import { getData } from "../actions/index";

export class Post extends Component {
  constructor() {
    super();
  }

  componentDidMount() {
    this.props.getData();
  }

  render() {
    return (
      <ul className="list-group list-group-flush">
        {this.props.articles.map(el => (
          <li className="list-group-item" key={el.id}>
            {el.title}
          </li>
        ))}
      </ul>
    );
  }
}

function mapStateToProps(state) {
  return {
    articles: state.remoteArticles.slice(0, 10)
  };
}

export default connect(
  mapStateToProps,
  { getData }
)(Post);

Save and close the files, and everything should work fine:

React Redux tutorial: asynchronous actions in Redux with Redux Thunk

Look at that! Good job!

To recap: Redux does not understand other types of action than a plain object. If you want to move asynchronous logic from React to Redux and being able to return functions instead of plain objects you have to use a custom middleware.

redux-thunk is a middleware for Redux. With redux-thunk you can return functions from action creators, not only objects. You can do asynchronous work inside your actions and dispatch other actions in response to AJAX calls.

When to use redux-thunk? redux-thunk is a nice middleware that works very well for simpler use cases. But if your asynchronous logic involves more complex scenarios then redux saga might be a better fit.

And in the next section we’ll finally take a look at Redux Saga. Hold tight!

An exercise for you: try to clean up your actions creators file by moving your async actions inside a custom middleware.

Another exercise for you: I didn’t account for errors in our fetch call. Do it if you have time!

React Redux tutorial for beginners: introducing Redux Saga

React Redux tutorial: writing your first Redux Saga

redux thunk makes perfect sense for a lot of project. In fact I encourage starting with redux thunk when you feel the need to manage async flow in Redux. No need to overcomplicate things. You can also entirely skip redux thunk and move your asynchronous logic to a custom middleware. But in reality asynchronous actions can be trickier to test and organize.

For this reason most developers prefer an alternative approach: redux saga.

What is redux saga? redux saga is a Redux middleware for managing side effects. The idea with redux saga is that of having a separate thread in your application for dealing with impure actions: API calls, storage access.

redux saga is different from an async action in terms of both syntax and code organization. With redux thunk you can put an API call directly inside an action creator while in redux saga you can have clear separation between synchronous and asynchronous logic. And that logic will be totally separated from your Redux code.

Moreover redux saga does not use regular JavaScript function. You will see a lot of asterisks and yield in your sagas.

Before moving further let’s look for a moment at what those asterisks mean!

Redux Saga and generator functions

What is a saga? In terms of JavaScript code a redux saga could be a single file containing:

  • a worker function
  • a watcher function

We will see what they do in the next section but for now take note: those functions are called sagas and the main difference from regular functions is that sagas are based on generator functions.

Generator functions in JavaScript had been added in ES6 (2015). In brief, a generator function is a JavaScript function which can be paused and resumed during its execution.

JavaScript function cannot be paused. Consider this loop:

function classicLoop() {
    for (var i = 0; i < 15; i++) {
        console.log(i)
    }
}

If you run the function the output of this code will be 1,2,3,4,5 … 15. There is no way to stop the loop from the outside. A generator function on the other hand makes possibile to control the loop “on demand”.

The main difference between regular functions and generator functions in JavaScript is in the syntax too. Generator functions are denoted with an asterisk and make use of the yield keyword. Let’s rewrite our code to use a generator function:

function* generatorLoop() {
    for (var i = 0; i < 15; i++) {
        yield console.log(i)
    }
}

First thing first I cannot run this function with generatorLoop(). For using the generator I must capture it in a variable and from there I can access the loop step after step with next():

var myGenerator = generatorLoop()

myGenerator.next()
myGenerator.next()
myGenerator.next()
myGenerator.next()

It’s easy to imagine what the output will be: first call to next 1, second call to next 2 and so on.

So generator functions in JavaScript are function which can be paused and resumed on demand. redux saga relies heavily on generator functions but the good thing is that you won’t need to call next() in your code. redux saga handles that for you under the hood.

And in the next section we’ll finally implement our first redux saga!

For learning more about generator functions take a look at function* on MDN.

Writing your first Redux Saga

In the previous sections we built a Post component which calls this.props.getData upon mounting to the DOM. getData is an asynchronous Redux action based on Redux thunk. That action is in charge for getting data from the remote API.

In this section we will refactor our code to use a Redux saga instead of a thunk. I won’t cover the entire Saga API in this post so please bear with me. We’ll just take a look at a bunch of methods.

Before getting started install redux saga with:

npm i redux-saga --save-dev

Now we can refactor our async action and remove the fetch call. From now on our action creator will just dispatch a plain action. Open up src/js/actions/index.js and modify getData to return a plain action named DATA_REQUESTED:

export function getData() {
  return { type: "DATA_REQUESTED" };
}

This very DATA_REQUESTED action will be “intercepted” by Redux saga with the takeEvery method. You can imagine takeEvery “taking” every DATA_REQUESTED action passing inside our app and starting some work in response to that action.

Earlier we saw that a redux saga could be a single file containing:

  • a watcher function
  • a worker function

The watcher is basically a generator function “watching” for every action we are interested in. In response to that action, the watcher will call a worker saga, which is another generator function for doing the actual API call.

The worker saga will call the remote API with the call method from redux-saga/effects. When the data is loaded we can dispatch another action from our saga with the put method, again, from redux-saga/effects. Makes sense?

Armed with this knowledge we can lay down our first redux saga! First create a new folder for holding your sagas:

mkdir -p src/js/sagas

and then create a new file named api-saga.js in src/js/sagas. And here’s our saga:

import { takeEvery, call, put } from "redux-saga/effects";

export default function* watcherSaga() {
  yield takeEvery("DATA_REQUESTED", workerSaga);
}

function* workerSaga() {
  try {
    const payload = yield call(getData);
    yield put({ type: "DATA_LOADED", payload });
  } catch (e) {
    yield put({ type: "API_ERRORED", payload: e });
  }
}

Let’s break down the logic flow of our saga. We can read the code like so:

  • take every action named DATA_REQUESTED and for each action of that type spin a worker saga
  • inside the worker saga call a function named getData
  • if the function does not result in any error then dispatch (put) a new action named DATA_LOADED, alongside with a payload
  • if the function results in an error then dispatch (put) a new action named API_ERRORED, alongside with a payload (the error)

The only thing we’re missing in our code is the getData function. Open up src/js/sagas/api-saga.js again and add the function:

import { takeEvery, call, put } from "redux-saga/effects";

export default function* watcherSaga() {
  yield takeEvery("DATA_REQUESTED", workerSaga);
}

function* workerSaga() {
  try {
    const payload = yield call(getData);
    yield put({ type: "DATA_LOADED", payload });
  } catch (e) {
    yield put({ type: "API_ERRORED", payload: e });
  }
}

function getData() {
  return fetch("https://jsonplaceholder.typicode.com/posts").then(response =>
    response.json()
  );
}

And finally we can wire up redux saga to our redux store. Open up src/js/store/index.js and update the store as follows:

// src/js/store/index.js
import { createStore, applyMiddleware, compose } from "redux";
import rootReducer from "../reducers/index";
import { forbiddenWordsMiddleware } from "../middleware";
import createSagaMiddleware from "redux-saga";
import apiSaga from "../sagas/api-saga";

const initialiseSagaMiddleware = createSagaMiddleware();

const storeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

const store = createStore(
  rootReducer,
  storeEnhancers(
    applyMiddleware(forbiddenWordsMiddleware, initialiseSagaMiddleware)
  )
);

initialiseSagaMiddleware.run(apiSaga);

export default store;

Worth noting in this file the createSagaMiddleware method and initialiseSagaMiddleware.run for running our saga.

Now close and save the file. Run npm start and ta-da! You should see the exact same output again with the remote posts correctly displaying in the browser.

Congratulations! You created your first redux saga!

An exercise for you: our reducer was ready for handling DATA_LOADED alonside with its payload. Complete the reducer for dealing with API_ERRORED.

An exercise for you: move DATA_LOADED, API_ERRORED, and DATA_REQUESTED inside named constants.

An exercise for you: do we need to better account for fetch errors inside getData?

CODE: you can access the complete example at react-redux-tutorial on Github. Clone the repo and checkout the most recent branch:

git clone https://github.com/valentinogagliardi/react-redux-tutorial
cd react-redux-tutorial
git checkout your-first-redux-saga

Redux: where are we now?

The Redux ecosystem is evolving. A couple of new APIs appeared since I first wrote this guide.

First, react-redux gained hooks. It’s an alternative to using connect() for wiring your React components with Redux. The new API is completely optional, suited for new React codebases relaying exclusively on functional components and hooks.

A more interesting news is the rise of redux-starter-kit, an opinionated layer over the infamous Redux boilerplate. Check it out!

Wrapping up

What a journey! I hope you learned something from this guide. I tried my best to keep things as simple as possible. I would love to hear your feedback in the comments below!

Redux has a lot of boilerplate and moving parts. Don’t get discouraged. Pick Redux, play with it and take your time to absorb all the concepts. I went from zero to understanding Redux by small steps. You can do it too!

Also, take your time to investigate why and if you should use Redux in your application. Either way think of Redux as an investment: learning it is 100% worthwhile.

Thanks for reading and stay tuned on this blog!

Hi! I’m Valentino! Educator and consultant, I help people learning to code with on-site and remote workshops. Looking for JavaScript and Python training? Let’s get in touch!

Source: Valentinog

LEAVE A REPLY

Please enter your comment!
Please enter your name here

This site uses Akismet to reduce spam. Learn how your comment data is processed.