Building a Simple React App - Part 3
Tags: react, app
Published: 2020-03-13 08:20:19

---
title: Building a Simple React App - Part 3
published: true
description: This is the third part of the series of articles on the topic of how to build a simple react app.
tags: react, app
---

This is the third part of our series about building simple react application. In this part, our main topic will be connecting our application to RESTful API. For that, we will need to use **async actions**, another important concept. If you haven't read previous parts, you can find them on following links [part 1](https://kolosek.com/building-simple-react-app-part-1/), [part 2](https://kolosek.com/building-simple-react-app-part-2/).

## Async actions

In order to use async actions, we need to inject middleware called [thunk](https://github.com/gaearon/redux-thunk). Thunk allows us to write async actions (action creators). As you know, until now all actions just returned simple action object, which would be dispatched automatically. With the thunk, we get a possibility to control what and when will be dispatched, it provides us possibility to return the function from action which can call dispatch manually. You will see in a second what that means to us. First let's add that middleware, while we are here, we will add one more middleware ([redux-logger](https://github.com/evgenyrodionov/redux-logger)) which will log each action as it gets dispatched along with application state before and after that action, pretty nice for debugging. First of all, install this two packages.

```bash
npm install --save redux-thunk redux-logger
```

And then inject them into the application.

```javascript
// src/index.js

...
import { createStore, applyMiddleware } from 'redux'

import thunk from 'redux-thunk';
import logger from 'redux-logger';
...

let store = createStore(
  appReducer,
  applyMiddleware(logger, thunk)
);
...
```

So we just imported two middlewares we want to inject, added `applyMiddleware` function from *redux*. Inside `createStore` we added the second parameter where we defined which middlewares we want to be injected (applied). Ok, now when we resolved that, let's add our first async action.

### Setup RESTful API server

We don't want our todos to be defined in the initial state, on our front-end, we want them to be fetched from some external resource. Instead of writing our RESTful API here we will use [json-server](https://github.com/typicode/json-server). It is quite simple to setup, we will go through that process right now. First, we need to install `json-server`

```bash
npm install -g json-server
```

Then create `db.json` file which will represent our database, and `json-server` will create all CRUD actions over our resources defined in that file, and will change that file immediately. It is a great tool for front-end testing. We will create `db.json` file inside our project, just to group all stuff into one place.

```json
// db.json

{
  "todos": [
    {
      "id": 1,
      "task": "This is simple API test task",
      "done": false
    },
    {
      "id": 2,
      "task": "This is simple API test task 2",
      "done": false
    },
    {
      "id": 3,
      "task": "This is simple API test task 3",
      "done": true
    }
  ]
}
```

This file is placed in the top folder (with `package.json` and `README.md`). If you take a look at this structure, you will see that it is pretty similar to one we've defined in reducers initial state (only task texts are different). Now we will start the server. Open new terminal tab and type:

```bash
# cd path-to-project/
json-server -p 9000 --watch db.json
```

You should see something like this.

And that is all, now you have all of the CRUD operations on `todo` resource, which are available through [localhost:9000](http://localhost:9000). Now we can really write our first async action, which would be fetching all todos and putting them into our state.

### First async action and fetching data from API

```
// src/components/Home/TodoList/actions/todoActions.js

export const fetchTodosStart = () => ({
  type: types.FETCH_TODOS_START
});

export const fetchTodosError = (error: Error) => ({
  type: types.FETCH_TODOS_ERROR,
  error
});

export const fetchTodosSuccess = (todos: Array) => ({
  type: types.FETCH_TODOS_SUCCESS,
  payload: { todos }
});

export const fetchTodos = () => dispatch => {
  dispatch(fetchTodosStart());

  fetch(`${API_URL}/todos`)
    .then((response) => response.json())
    .then((body) => dispatch(fetchTodosSuccess(body)))
    .catch((error) => dispatch(fetchTodosError(error)));
}
```

We practically created four actions (action creators), three are simple actions returning just a action object, and one is async (`fetchTodos`) which dispatches other three when it should. We could theoretically use any of these three simple actions directly, but we won't need that. `fetchTodosStart` is simple action which purpose is just to notify system that `fetchTodos` action has started, `fetchTodosError` notifies system that some error occurred while *fetching todos*, and `fetchTodosSuccess` notifies system that todos are fetched and passes those fetched todos in action object.

Nothing new here, now let's take a look at `fetchTodos`. The first thing to note here is that this action doesn't return a simple object **but a function**, with *dispatch* as a parameter (*getState* is another parameter provided by thunk, but we don't need that here, so we don't store it anywhere). At the beginning, we dispatch signal that fetching has started. Then we do real fetch using `fetch` method from the native framework. If everything goes well, we dispatch success signal sending response body as a value of *todos* parameter, and if any error (`catch` part), we just dispatch error signal providing that error as a parameter. Nothing complicated, right? That is it, we created async action, which fetches data from the server, parses it (`response.json()` part) and notifies system on each *"breakpoint"*. This pattern with three simple actions (as a help) will be followed by this article. It is not mandatory, you could do something like

```
fetch(`${API_URL}/todos`)
  .then((response) => response.json())
  .then((body) => dispatch({
    type: types.FETCH_TODOS_SUCCESS,
    payload: { todos: body }
  })
  .catch((error) => dispatch({
    type: types.FETCH_TODOS_ERROR,
    payload: { error }
  });
```

But I find it more readable when it is separated. We haven't yet defined `API_URL` constant.

```javascript
// src/utils/configConstants.js

export const API_URL = 'http://localhost:9000';
```

And of course, we need to import that constant in `todoActions.js`

```javascript
// src/components/Home/TodoList/actions/todoActions.js

import { API_URL } from '../../../../utils/configConstants';
```

Right now we are getting an error in our front-end application (`Failed to compile. "export 'FETCH_TODOS_SUCCESS' (imported as 'types') was not found in '../constants'."`) That is because we haven't defined constants, and yet we use them. So let's define that.

```javascript
// src/components/Home/TodoList/constants.js

export const FETCH_TODOS_START = 'FETCH_TODOS_START';
export const FETCH_TODOS_ERROR = 'FETCH_TODOS_ERROR';
export const FETCH_TODOS_SUCCESS = 'FETCH_TODOS_SUCCESS';
```

Next step is to add reducer handler for this action(s), otherwise, all those signals would be useless. We will do this just by adding new case inside `todoReducer`.

```javascript
case types.FETCH_TODOS_SUCCESS:
  return state.set('todos', [...action.payload.todos]);
```

Nice and simple, just exchange state.todos with the new array containing data received from action object. We haven't handled `FETCH_TODOS_ERROR` and `FETCH_TODOS_START`, currently, they are not in our main focus. You could handle error signal in some global way, or locally to your todoReducer, it depends on you, however you want to. Start signal can be useful for something like rendering loading the bar on screen or disabling some option until action is finished, just notice that no `END` signal is sent, so you will have to handle end on success and on error. The circle is now complete, all we need to do now is to actually make a use of this.

We won't need anymore that initial state defined in todoReducer (that was just test data), so let's delete it.

```
// src/components/Home/TodoList/reducers/todoReducer.js

...
const TodoState = new Record({
  todos: []
});
...
```

If you look at your application now, there won't be any todos on the screen, exactly what we wanted. Now let's fetch. Where would we add this part of the the code. If you remember from [last part](https://kolosek.com/building-simple-react-app-part-2/) where we talked about **presentational** and **container** components, we said that those container components should handle data fetching, so we need to change our `TodoListContainer`.

```javascript
// src/components/Home/TodoList/TodoListContainer.jsx

import React, { Component } from 'react';

import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';

import { setTodoDone, deleteTodo, addTodo, fetchTodos } from './actions/todoActions';
import TodoList from './TodoList';

class TodoListContainer extends Component {

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

  render() {
    return <TodoList {...this.props} />
  }

}

const mapStateToProps = state => ({
  todos: state.todoReducer.todos
});

const mapDispatchToProps = dispatch => bindActionCreators({
  setTodoDone,
  deleteTodo,
  addTodo,
  fetchTodos,
}, dispatch)


export default connect(mapStateToProps, mapDispatchToProps)(TodoListContainer);
```

Most parts stayed the same, we linked `fetchTodos` action in our `mapDispatchToProps` (and imported it at the top). But now simple `connect` wrapper isn't enough for us, we need something more, something that will actually fetch data in some moment. That's why we created a new component (real `TodoListContainer`) and used **lifecycle** method `componentDidMount` in which fetching is actually called. Its render method is just simple returning `TodoList` with all received props sent down. So it is still just a wrapper, only *"smart"* wrapper which does something before rendering wrapped component. Now if you go to your browser and look at the application you should see three todos defined in our `db.json`.


And our logger middleware is logging each action on our console, as you can see, only `FETCH_TODOS_START` and `FETCH_TODOS_SUCCESS` is logged (first logged action you can disregard, it is just a log for `fetchTodos` which doesn't actually need to be logged). If you try to add, modify or delete any todo now, it will still work as it worked before, but won't be saved to the database, that is because those actions just change reducer, neither one is actually *"talking"* to an external source (API), let's fix that.

### Adding new todo

```javascript
export const addTodoStart = () => ({
  type: types.ADD_TODO_START
});

export const addTodoError = (error: Error) => ({
  type: types.ADD_TODO_ERROR,
  error
});

export const addTodoSuccess = (todo: Object) => ({
  type: types.ADD_TODO_SUCCESS,
  payload: {
    todo
  }
})

export const addTodo = (task: String) => dispatch => {
  dispatch(addTodoStart());

  fetch(`${API_URL}/todos`, { 
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      task,
      done: false,
    })
  })
    .then((response) => response.json())
    .then((body) => dispatch(addTodoSuccess(body)))
    .catch((error) => dispatch(addTodoError(error)));
}
```

We replaced `addTodo` action with an async one, also we added already familiar three methods (start, error and success actions) as helpers. The interesting thing here is that todo creating is moved from reducer into the action, actually, it is moved to API, but because of default API behavior we have to provide all parameters (can't create default value on API - which is what we would do in a real application). It is pretty much same as `fetchTodo` action, on start it dispatches start signal, after that, it hits API endpoint, only difference is that here we need to send `POST` method, set header for `Content-Type` so that API knows how we formatted data which we send, and last but not least we need to send real data in `body` as JSON encoded string. After that, we get a response, parse it as JSON, and dispatch body as new todo object with success signal, or in case of error, just dispatch error signal with that error. Why we dispatch value returned from the server instead of object we created? Simple, the server will automatically create an `id`, which we need for modification and removal, so we need to wait for the server to give us complete object, which we will then store in reducer. Let's see reducer modifications to support this.

```javascript
// old case
case types.ADD_TODO:
  return state.set('todos', [ ...state.todos, { id: getLastId(state.todos) + 1, task: action.payload.task, done: false } ]);

// new case
case types.ADD_TODO_SUCCESS:
  return state.set('todos', [...state.todos, action.payload.todo]);
```

It is actually simplified, reducer doesn't need to generate id, or an object anymore (it shouldn't generate resources anyway). That is it. Try now adding new todo and refreshing page, it persists.

### Deleting todo

```javascript
export const deleteTodoStart = () => ({
  type: types.DELETE_TODO_START,
});

export const deleteTodoError = (error: Error) => ({
  type: types.DELETE_TODO_ERROR,
  error
});

export const deleteTodoSuccess = (id: Number) => ({
  type: types.DELETE_TODO_SUCCESS,
  payload: {
    id
  }
});

export const deleteTodo = (id: Number) => dispatch => {
  dispatch(deleteTodoStart());

  fetch(`${API_URL}/todos/${id}`, {
    method: 'DELETE',
  })
    .then((response) => dispatch(deleteTodoSuccess(id)))
    .catch((error) => dispatch(deleteTodoError(error)));
}
```

As it goes for `deleteTodo`, it is pretty much the same. Helper methods (actions) are there, same as always, nothing new there, and bind-together action `deleteTodo` is also same as others, only difference is http method, and the fact that we don't need to parse response body (it is empty), we just need to know that response was returned successfully without error (valid status code), and we can dispatch success signal. Reducer hasn't changed at all, the only thing that changed is the name of constant on which handler is called, renamed from `DELETE_TODO` into `DELETE_TODO_SUCCESS`.

### Updating todo

```javascript
export const setTodoDoneStart = () => ({
  type: types.SET_TODO_DONE_START
})

export const setTodoDoneError = (error: Error) => ({
  type: types.SET_TODO_DONE_ERROR,
  error
});

export const setTodoDoneSuccess = (id: Number, done: Boolean) => ({
  type: types.SET_TODO_DONE_SUCCESS,
  payload: {
    id,
    done
  }
})

// Changed from id: Number into todo: Object to use PUT /todos/:id, avoid creating custom api routes
export const setTodoDone = (todo: Object, done: Boolean) => dispatch => {
  dispatch(setTodoDoneStart());

  fetch(`${API_URL}/todos/${todo.id}`, {
    method: 'PUT',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ ...todo, done })
  })
    .then((response) => dispatch(setTodoDoneSuccess(todo.id, done)))
    .catch((error) => dispatch(setTodoDoneError(error)));
}
```

Same goes for `setTodoDone`, everything stays the same as before. Here we use `PUT` method, to use default API update method because we are avoiding custom API routes (in a real application you would probably have separate route only for setting done, which would get only an `id`). Reducer hasn't been changed for this either (only constant name). For this we have to change a little bit call to the method (because we changed the interface, it doesn't get the only id anymore), so we need to modify a little bit `Todo` component. Inside `Todo` render method we just need to change our `setDone` handler, instead of `() => setDone(todo.id, !todo.done)`, we want `() => setDone(todo, !todo.done)`. And that is all. Now we completely migrated our application to use RESTful API for all data operations.

## Conclusion

In this part, we connected our application to RESTful API and adapted all actions to actually hit API endpoints, and change data on the server. One thing you could do in a real application is to extract `fetch` call into a helper method (or a class) so that you can easily replace the library that you are using for http requests. Another thing that may be useful in real examples is [normalizr](https://github.com/paularmstrong/normalizr), it won't be discussed here but I encourage you to take a look. [Next part](https://kolosek.com/building-simple-react-app-part-4/) will be the final part of this series, and it will show you usage of **selectors**, and also we will focus a little on application styling. 

*Originally published at [Kolosek blog](https://kolosek.com/building-simple-react-app-part-3/).*