Building a Todo app using React, Redux and Rails

Background

In my previous story, we built a simple Todo List app using Rails API as the backend and React for the frontend. In this tutorial, we are going to integrate Redux with our application in order to better manage the state of the app. So, if you want to follow this one, I suggest you read the first part before you start reading this one.

Redux

What is Redux and what does it do? Redux is a state manager which can replace the current React state manager. As you remember, we used this.state to access the data within our app such as the server JSON response. Also, we used this.setState to store data. Redux does the same thing but in a more consistent and scalable way.

But what problem(s) does Redux solve? Well, if your app has many components (Todos, ShoppingItems, UserList, …), you may need to pass the state down to those components for the sake of data flow, which is not really scalable as the app may grow with many components. Another problem is that some components may not even need the transferred data.

With Redux, there is a global state — known as store — and components can subscribe to that store to fetch data. I suggest that you read the core concepts of Redux.

Ok it’s time to start coding! Let’s install the required libraries:

npm install --save redux react-redux redux-thunk

We need to know about 3 concepts before integrating Redux with our React app: Actions, Reducer, Store. Let’s start with Actions.

Actions

An action is basically an event descriptor. Actions (some prefer to use the term: ActionTypes) should have at least a type property (with or without any kind of data). Let’s see a simple action:

{ 
type: 'LOAD_TODOS',
todos: todos
}

Later when we introduce the concept of Reducer, we will use this notation to update the state of our app. The above example is saying: it’s time to load todo items (and maybe to display them). It’s the first piece of the puzzle so wait for the next steps. Let’s create a file in src/actions/actionTypes.js:

We just need 4 actions in our app. Next, we define 4 functions to return those actions. Let’s create src/actions/actionCreators.js:

Reducer

It’s time to introduce another piece of Redux: Reducer. A reducer is a simple function with 2 arguments: state, action. It returns the new state based on the current state and the provided action. Create a file in src/reducers/todosReducer.js with the following content:

So, a reducer is expected to check the action and return the new state. For example, when we ask the store to show/update the todo list, we pass the LOAD_TODOS action to the reducer (it’s called dispatching and we’ll see how to do it) and the reducer will return the list of todos as the new state and the app shows the items one by one in a loop.

In our app we just need one reducer to update the state, however, in large applications there will be more than one and we’ll need to combine them as a single reducer which we call root reducer. Using the built-in combineReducers function of Redux, we combine our reducers. Now, let’s create src/reducers/rootReducer.js:

Store

The last concept we need to know about is Store. Redux store is an object in which the state of an application is stored. It provides functions for components to fetch and update the state of the app. The main benefit of the store for components is that parents don’t need to pass the state (data) via props to child components. Let’s create the store of our app in src/store.js:

We used Redux Thunk to be able to define action creators — which are functions — instead of an action. It will all make sense when we connect every piece of the puzzle!

Now let’s import the store in index.js:

The store is ready to use! We feed the created store to the Provider component of Redux which encompasses the main component of our React app — which is <App/>. That’s how we integrate Redux with our existing app.

Container

There is only one container in our app which handles the CRUD operations. It’s almost the same as what we created in our previous story with minor changes to utilise the Redux store.

We need to pass the actions we defined to the reducer so that it can return the new state to update the state of the app. It’s called dispatching and we use the dispatch function of the Redux store to accomplish this step:

dispatch(loadTodos(data))

Before we can call this function, we need to use the connect function of Redux which allows us to use dispatch as a property like this:

this.props.dispatch(loadTodos(data))

Our connect function accepts 1 argument which is a function that maps the state to the props object:

const mapStateToProps = (state) => {   return {      todos: state.todos   }}export default connect(mapStateToProps)(TodosContainer)

Now let’s see the full code in src/containers/TodosContainer.js:

Also, I used React ref to access the input element with this.getTitle.value in the functions:

<input className="taskInput" type="text" 
placeholder="Add a task" maxLength="50"
onKeyPress={this.createTodo}
ref={(input)=>this.getTitle = input}
/>

Now everything is wired up and ready to use. When an item is created, we dispatch the ADD_TODO action and the reducer returns a new array (state) which contains the previous state and the new todo item:

case ADD_TODO:   return [      ...state,      {         id: action.id,         title: action.title,         done: false      }   ];

Or when we delete an item, the reducer simply removes that item from the state:

case DELETE_TODO:   return state.filter(todo => todo.id !== action.index);

Recap

Following the last story about how to create a React-Rails app, in this story we used Redux for the state management of our app. Redux helps us build scalable applications by providing a simple architecture: store-reducer-action.

For the sake of simplicity, the main container of this app was also responsible to render the elements (usually the job of a component), so that we could focus on Redux concepts and the way it manages the state. In the repo, you’ll find both components and containers which were separated to handle presentational and state/behaviour tasks, respectively. You can read Dan Abramov article for more information.

I hope you enjoyed this article. You can download the source code from my Github: pamit/react-redux-rails-todo.

UPDATE: I updated the code to include login/logout functionality which can be accessed at pamit/react-redux-loginform and at Heroku demo (Check out seeds.rb for username/password).

Happy coding!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store