ready to build your app

Using React with Redux: Beginner’s Guide

Have you noticed that React JS is rarely mentioned in a sentence without Redux?

So did I when I started learning React. But with the sheer amount of new frameworks and libraries coming out every week in the JavaScript world, it might not be the best idea to jump straight away into the “Getting Started” section in a package’s documentation without knowing whether you really need it.

However, Redux isn’t by any means another buzzword. It’s been around since 2015 and there’s a reason behind its popularity. Whether you haven’t heard of it yet, or you don’t know why you should use it in your React project, you’re in the right place.

We’re going to build a simple app so you can see React with Redux together in action.

This guide assumes you have basic knowledge of React. If you don’t, check out the resources to learn React that we recommend.

What is Redux?

Redux is a predictable state container for JavaScript applications. The main concept behind it is to store the entire state of an application in one, centralized location. But how does Redux make the state predictable? In order to change the state, Redux forces you to dispatch actions, which are plain JavaScript objects. That means, if the state changed, an action must have been dispatched.

What ties states and actions together are functions called reducers. Again, these are plain JavaScript functions that take the application’s state and action as parameters and return the next state. The great thing about reducers is that they are pure functions. They don’t mutate the state object, making time-travel debugging in Redux DevTools possible (we’ll cover it later in the article, stay tuned).

It’s worth mentioning that Redux is a standalone library and it’s pretty flexible. You can use it with React, Angular, jQuery, or even vanilla JavaScript. It works particularly well with React though, because React lets you describe the UI as a function of state – and state management is what Redux does best.

The need for a state management tool

At first glance, it might seem that React provides you with all the state management you’ll ever need. Any component can be stateful and you can easily pass any data through props. Adding in Redux to your project without experiencing a problem with React’s data flow first could be a little confusing. Let’s take a look at the chart below:

React with Redux state management.

This is an example of React’s unidirectional data flow. In this example, parent component provides children components with a snapshot of its state through read-only attributes called “props”. “Component A” and “Component B” share the same data, passed down from “Parent Component”. The next example shows where the issues begin.

React with Redux components.

Can you see where this is going? This is something that happens often in React projects, not even necessarily large ones. If “Component C” and “Component D” need the same data, you have to pass the data from their nearest common ancestor, which happens to be “Parent Component”. The data travels through several levels of intermediate components, which might not even need the data.

Passing data in this manner can lead to many issues. Every component in this structure is tightly coupled now, making you unable to move components freely. It can also affect your app’s performance because every state update would cause all children components to re-render. If you end up in a situation where two components need the same data, you often need to modify the code of several components to set up the data flow from top to bottom of the tree – it is time-consuming and often annoying.

This is where Redux comes in. It solves problems described above by providing a centralized data store, which is nothing more than a JavaScript object with a few methods on it. This allows you to keep the data in one place, and access it anywhere in your application. Let’s take a look at how it can be visualized in our React app scenario:

React with Redux data store.

As you can see, with Redux you no longer have to pass the data through multiple levels of nested components. It can be accessed anywhere in the application. Components that need the data, can access it directly from the store. Fewer components are involved with the data flow and the code becomes simpler.

How to build an example app using React with Redux

In order to better understand how to use React and Redux together, we’ll build an example app. It will be a simple counter showing its current state and allowing the user to increase and decrease its value. If you don’t feel like going through all the steps yourself, you can clone this repository.

Step 1

For this simple example we’ll use create-react-app. You can skip this step if you have it installed already.

1
npm install -g create-react-app

Step 2

Create a new app:

1
create-react-app react-redux-counter

This one will take a moment. After it’s done, enter your project’s directory:

1
cd react-redux-counter

Step 3

We’re going to need Redux (obviously) and the react-redux package, which is the official React binding for Redux. Let’s install both:

1
npm install redux react-redux --save

Step 4

For simplicity’s sake, let’s create all files and directories needed for this guide now:

1
2
mkdir -p src/store && touch src/store/action-types.js && touch src/store/action-creators.js && touch src/store/reducers.js
mkdir -p src/components && touch src/components/Counter.js && touch src/components/counter.css

Step 5

In src/store/action-types.js, let’s describe the possible types of actions that can occur in our application:

1
2
export const TYPE_INCREMENT = 'INCREMENT'
export const TYPE_DECREMENT = 'DECREMENT'

This is pretty much self-explanatory. We can either increment or decrement our counter.

Step 6

In src/store/action-creators.js, we’ll define our action creators. As you may remember, actions are plain JavaScript objects that describe what happens in our application.

1
2
3
4
5
6
7
8
9
import { TYPE_INCREMENT, TYPE_DECREMENT } from './action-types'

export const increment = () => ({
  type: TYPE_INCREMENT,
})

export const decrement = () => ({
  type: TYPE_DECREMENT,
})

As you can see, these are regular functions that return objects (actions) with a type property. We’ll do different things based on action types in our reducers. In more complicated examples, actions would carry additional data, but only the type property is obligatory and it’s sufficient for our example.

Step 7

In src/store/reducers.js, we define our app’s initial state and our reducer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { TYPE_INCREMENT, TYPE_DECREMENT } from './action-types'

const initialState = {
  counter: 0,
}

export const counterReducer =  (state = initialState, action) => {
  switch (action.type) {
    case TYPE_INCREMENT:
      return {
        ...state,
        counter: state.counter + 1,
      }
    case TYPE_DECREMENT:
      return {
        ...state,
        counter: state.counter - 1,
      }
    default:
      return state
  }
}

In a Redux application, every time an action is dispatched, redux will call every reducer, passing current state as first parameter and recently dispatched action as second parameter. Our simple app is going to have one reducer. Based on action type we do three different things here:

  • return new state with increased counter
  • return new state with decreased counter
  • return state unchanged

Simple, right? Notice how in these files we haven’t imported anything from redux or react-redux, this is just JavaScript so far.

Step 8

It’s time to combine React with Redux. Go to src/index.js and replace its content with this piece of code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import { counterReducer } from './store/reducers';
import App from './App';

const store = createStore(
  counterReducer,
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
);

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root'),
);

This is our app’s entrypoint. It was already defined by create-react-app. There are a couple of changes we’re making. We’re using the createStore function to create our store, passing our reducer to it. Then we’re wrapping the App component in a Provider component which will make the store available for our React app.

Step 9

Let’s create our actual Counter component in src/components/Counter.js:

1
2
3
4
5
6
7
8
9
10
11
12
import React from 'react'
import { connect } from 'react-redux'
import { increment, decrement } from '../store/action-creators'
import './counter.css'

export const CounterComponent = ({ counter, handleIncrement, handleDecrement }) => (
  <div>
    <p className="counter">Counter: {counter}</p>
    <button className="btn btn-increment" onClick={handleIncrement}>+</button>
    <button className="btn btn-decrement" onClick={handleDecrement}>-</button>
  </div>
)

CounterComponent is a presentational component that shows the current counter and calls handleIncrement or handleDecrement on button clicks. We take this stateless functional component and use connect from react-redux to make our store available to it:

1
2
3
4
5
6
7
8
9
10
11
12
13
const mapStateToProps = ({ counter }) => ({
  counter,
})

const mapDispatchToProps = {
  handleIncrement: increment,
  handleDecrement: decrement,
}

export const Counter = connect(
  mapStateToProps,
  mapDispatchToProps,
)(CounterComponent)

mapStateToProps is a function that takes state as its argument. We use ES6 destructuring to get “state.counter” and we return an object that maps the piece of state that we need to component’s props.

mapDispatchToProps is an object that maps our action creators to component’s props.

Then we pass both to connect, which returns a new function that will take in our CounterComponent and return a connected component. Notice how our component doesn’t have access to store directly – connect does it for us under the hood.

Step 10

Add basic styles in src/components/counter.css:

1
2
3
4
5
6
7
8
.btn, .counter {
  font-size: 1.5rem;
}

.btn {
  cursor: pointer;
  padding: 0 25px;
}

And then replace the contents of src/App.js to render our component:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React, { Component } from 'react';
import { Counter } from './components/Counter'

class App extends Component {
  render() {
    return (
      <div className="App">
        <Counter />
      </div>
    );
  }
}

export default App;

Now run npm start in your terminal. That’s it. In a few simple steps, we created a counter using React and Redux. If you want to see how the same app would be implemented without using Redux, you can clone the repo mentioned earlier and run git checkout without-redux.

Easy debugging with Redux DevTools

When talking about using React with Redux, we have to mention Redux DevTools. In order to use it, you need to install an extension for your browser. You can use the following links:

  • Chrome:
    https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=en
  • Firefox:
    https://addons.mozilla.org/en-US/firefox/addon/reduxdevtools/

After that, you should be able to see the Redux tab in your browser’s developer tools. Let’s open it up and see what we can do with it.

Easy debugging of React with Redux.

On the left side of the tools, you can see a list of actions that were dispatched since application start, along with time data. You can click on any action, to reveal ‘Jump’ and ‘Skip’ options, that let you time-travel to a point in time that the action was dispatched or skip the action’s effects respectively.

The right panel introduces a few more buttons that allow you to see the action’s type and the data it carries, the state tree at the time of the action picked from left-side menu, the difference between last and current state tree, and more.

The slider on the bottom also allows you to time travel through the dispatched actions. You can go back to a certain point in time and replay the actions that triggered state changes with a speed that you set.

These are just the basics of Redux DevTools, but these features are the easiest to start with and they are the ones you’ll use the most.

For a deeper dive into Redux DevTools, I highly recommend the article “Redux Devtools for Dummies” by codeburst:

https://codeburst.io/redux-devtools-for-dummies-74566c597d7

Do I always need Redux?

Definitely not. Small and simple projects work perfectly good without it. You’ve just seen that Redux adds a little boilerplate to set things up. You don’t need to overcomplicate your simple app (like we just did). Reach for Redux when you feel like you need it. If many pieces of state need to be shared between components that are far away from each other in component tree and state become difficult to manage – consider using Redux.

Approach it with caution like any other tool. If you decide to use React with Redux though, remember that it is not a must to use it for every piece of state. Local state is fine. Use store for variables that should be global for the application, not for everything. Always use the right tool for the job.

Final words

In this guide, you’ve learned what Redux is, why developers choose to use it and how to build an example app using React with Redux. Don’t stop there. Clone our repository, try to add new functionality to reset the counter, or see how our app would be built without Redux (checkout without-redux branch).

This should be just enough to get you started. For more info, check out the bonus resources at the bottom of this article. Happy coding!

Bonus resources:

  • Repository containing code from this guide:
    https://github.com/brainhubeu/react-redux-counter-example
  • Redux – official documentation:
    https://redux.js.org/introduction/getting-started
  • React-redux – official documentation:
    https://react-redux.js.org/introduction/quick-start
  • Redux DevTools explained:
    https://codeburst.io/redux-devtools-for-dummies-74566c597d7

Tomasz Bubała

Tomasz Bubała is a JavaScript Full Stack Developer at Brainhub (a software house building awesome Node.js web and mobile apps).