Build A Todo App with React, MongoDB, ExpressJS, and NodeJS Part 2 (Frontend)
Tags: javascript, webdev, react, node
Published: 2020-03-23 22:40:32

---
title: Build A Todo App with React, MongoDB, ExpressJS, and NodeJS Part 2 (Frontend)
date: "2020-03-23T22:40:32.169Z"
topic: webdev
description: Quick and easy tutorial on how to build a todo application with react, nodejs, and express
published: true
tags: #javascript, #webdev, #react, #node
canonical_url: https://www.devsurvival.com/todo-app-react-frontend/
series: "Todo App with React and Node"
---

Welcome back. Congratulation on completing <a target="_blank" href="/htnguy/build-a-todo-app-with-react-and-node-part-1-backend-4egl" ref="noopener noreferrer">part 1</a> of the tutorial on how to create a todo app with React and NodeJS.

In part 2, we will create the react frontend and connect it to our API backend to `GET, POST, UPDATE, and DELETE` our todos.

### Additional Packages

Before we can start coding, we have to install some additional packages to make this work.

1. **Axios** - allows us to send http request from out react frontend to our todo API run `npm install axios` in the `todo-frontend` directory
2. **Cors** - allows cross domain http request. In other words, without enabling cors on the backend, even Axios will not able to send our request to the API. run `npm install cors` in the `todo-backend` directory, and then add the snippet below to the top of your `index.js` file in the root of `todo-backend` directory

```javascript
const cors = require("cors")
app.use(cors())
```

### Almost There :)

Since the frontend for this application is pretty straight forward, we are going to make changes to two files: `App.js` and the `APIHelper.js` (we will have to create)

Let's create the `APIHelper.js` file in the `src` directory of the `todo-frontend` .

```shell
touch APIHelper.js
```

Copy the following code into the `APIHelper.js` file

```javascript
import axios from "axios"

const API_URL = "http://localhost:3000/todos/"

async function createTodo(task) {
  const { data: newTodo } = await axios.post(API_URL, {
    task,
  })
  return newTodo
}

async function deleteTodo(id) {
  const message = await axios.delete(`${API_URL}${id}`)
  return message
}

async function updateTodo(id, payload) {
  const { data: newTodo } = await axios.put(`${API_URL}${id}`, payload)
  return newTodo
}

async function getAllTodos() {
  const { data: todos } = await axios.get(API_URL)
  return todos
}

export default { createTodo, deleteTodo, updateTodo, getAllTodos }
```

#### Let Me Explain

We have four functions that mimic our API `createTodo, deleteTodo, updateTodo, getAllTodos` .

**`createTodo(task)`** - accepts a task and sends a post via `axios.post` to our `API_URL` and returns the newTodo. Note: axios stores the response of our requests in a field called `data`,

**`deleteTodo(id)`** - accepts an id and sends a delete request to our API.

**`updateTodo`** - accepts an id and a payload object contain fields that we want to update => `payload= {completed: true}` .It sends a `PUT` request to update the todo.

**`getAllTodos`** - fetching all the todos from our API via `axios.get`

And we make all these functions accessible in other files using an export function `export default { createTodo, deleteTodo, updateTodo, getAllTodos };`

#### App.js

Copy the following code into your `App.js` file

```javascript
import React, { useState, useEffect } from "react"
import "./App.css"
import APIHelper from "./APIHelper.js"

function App() {
  const [todos, setTodos] = useState([])
  const [todo, setTodo] = useState("")

  useEffect(() => {
    const fetchTodoAndSetTodos = async () => {
      const todos = await APIHelper.getAllTodos()
      setTodos(todos)
    }
    fetchTodoAndSetTodos()
  }, [])

  const createTodo = async e => {
    e.preventDefault()
    if (!todo) {
      alert("please enter something")
      return
    }
    if (todos.some(({ task }) => task === todo)) {
      alert(`Task: ${todo} already exists`)
      return
    }
    const newTodo = await APIHelper.createTodo(todo)
    setTodos([...todos, newTodo])
  }

  const deleteTodo = async (e, id) => {
    try {
      e.stopPropagation()
      await APIHelper.deleteTodo(id)
      setTodos(todos.filter(({ _id: i }) => id !== i))
    } catch (err) {}
  }

  const updateTodo = async (e, id) => {
    e.stopPropagation()
    const payload = {
      completed: !todos.find(todo => todo._id === id).completed,
    }
    const updatedTodo = await APIHelper.updateTodo(id, payload)
    setTodos(todos.map(todo => (todo._id === id ? updatedTodo : todo)))
  }

  return (
    <div className="App">
      <div>
        <input
          id="todo-input"
          type="text"
          value={todo}
          onChange={({ target }) => setTodo(target.value)}
        />
        <button type="button" onClick={createTodo}>
          Add
        </button>
      </div>

      <ul>
        {todos.map(({ _id, task, completed }, i) => (
          <li
            key={i}
            onClick={e => updateTodo(e, _id)}
            className={completed ? "completed" : ""}
          >
            {task} <span onClick={e => deleteTodo(e, _id)}>X</span>
          </li>
        ))}
      </ul>
    </div>
  )
}

export default App
```

#### Let Me Explain

We start by creating two states: `todo` and `todos`. States are like information about your components. `todo` will store the user input when creating a new todo and `todos` will store all of our todos.

Lets see what the component looks like on paper.

```javascript
return (
  <div className="App">
    <div>
      <input
        id="todo-input"
        type="text"
        value={todo}
        onChange={({ target }) => setTodo(target.value)}
      />
      <button type="button" onClick={createTodo}>
        Add
      </button>
    </div>

    <ul>
      {todos.map(({ _id, task, completed }, i) => (
        <li
          key={i}
          onClick={e => updateTodo(e, _id)}
          className={completed ? "completed" : ""}
        >
          {task} <span onClick={e => deleteTodo(e, _id)}>X</span>
        </li>
      ))}
    </ul>
  </div>
)
```

To keep things simple we have a text input, a button for submitting the input, and a list.

The text input has an `onChange` event handler for handling user inputs. When the user clicks the `Add` button, the `onClick` event handler is triggered- createTodo() is invoked.

#### Creating Todo

lets look at what the `createTodo` function does

```javascript
const createTodo = async e => {
  e.preventDefault()
  if (!todo) {
    // check if the todo is empty
    alert("please enter something")
    return
  }
  if (todos.some(({ task }) => task === todo)) {
    // check if the todo already exists
    alert(`Task: ${todo} already exists`)
    return
  }
  const newTodo = await APIHelper.createTodo(todo) // create the todo
  setTodos([...todos, newTodo]) // adding the newTodo to the list
}
```

Overall, it validates the input, create the todo using the `APIHelper.js` we created, and then add it to the list of `todos`

### Displaying the Todos

```javascript
<ul>
  {todos.map(({ _id, task, completed }, i) => (
    <li
      key={i}
      onClick={e => updateTodo(e, _id)}
      className={completed ? "completed" : ""}
    >
      {task} <span onClick={e => deleteTodo(e, _id)}>X</span>
    </li>
  ))}
</ul>
```

We are mapping over the list of `todos` and creating a new list item with `li`

How do we load the todos when the page loads? React offers a useful function call `useEffect` which is called after the component is rendered

```javascript
useEffect(() => {
  const fetchTodoAndSetTodos = async () => {
    const todos = await APIHelper.getAllTodos()
    setTodos(todos)
  }
  fetchTodoAndSetTodos()
}, [])
```

we create an `async function` called `fetchTodoAndSetTodos` which call the `APIHelper`'s `getAllTodos` function to fetch all the todos. It then sets the `todos` state of the component to include these todos.

#### Marking Todo As Completed

```javascript
;(
  <li
    key={i}
    onClick={e => updateTodo(e, _id)}
    className={completed ? "completed" : ""}
  >
    {task} <span onClick={e => deleteTodo(e, _id)}>X</span>
  </li>
)``
```

When the task is completed we add the class `completed`. you can declare this css class in a separate file. `create-react-app` provides an `App.css` file for this purpose.

```css
.completed {
  text-decoration: line-through;
  color: gray;
}
```

Notice each todo item (`<li onClick={updateTodo}>{task}</li>`) has an `onClick` event handler. When we click an `li` we trigger the `updateTodo` function.

```javascript
const updateTodo = async (e, id) => {
  e.stopPropagation()
  const payload = {
    completed: !todos.find(todo => todo._id === id).completed,
  }
  const updatedTodo = await APIHelper.updateTodo(id, payload)
  setTodos(todos.map(todo => (todo._id === id ? updatedTodo : todo)))
}
```

`e` is the event object on which we invoked `e.stopPropagation()` to prevent the click event from propagating to the parent element. Next, we find the todo in the list of `todos` and flip its completed status(`completed = true => !completed == false`) . We add this new `completed` status to the `payload` object. we then call `APIHelper.updateTodo` and pass in the `id` and `payload` of the todo.

The next bit of code is a little confusing. we call `todos.map` which maps over the array and return a new array. With each iteration we are check if the id matches. If it matches, then we return the `updatedTodo` which is effectively updating the todo. Otherwise, we return the original todo and leave it unchanged.

#### Deleting a Todo

```javascript
<li
  key={i}
  onClick={e => updateTodo(e, _id)}
  className={completed ? "completed" : ""}
>
  {task} <span onClick={e => deleteTodo(e, _id)}>X</span>
</li>
```

Notice how we have a `<span onClick={DeleteTodo(e, _id)}>X</span>` next to the task. When this span is clicked, it triggers the `deleteTodo` function that will delete the todo.

Here is the function for deleting the todo.

```javascript
const deleteTodo = async (e, id) => {
  try {
    e.stopPropagation()
    await APIHelper.deleteTodo(id)
    setTodos(todos.filter(({ _id: i }) => id !== i))
  } catch (err) {}
}
```

we call `APIHelper.deleteTodo` and pass in the id of the todo we want to delete. If you refresh the page, the todo will be deleted. What if you were lazy and did not feel like refreshing the page or you didn't know better? Well, we have to remove it manually from the `todos` state. We remove it by calling `todos.filter` which will filter out the todo with the id we just deleted.

### Show Time

Here is a quick demo:

{%youtube DYWab_nan-w%}

This tutorial's source code can be found on <a href="https://github.com/htnguy/todo-app-tutorial.git" target="_blank" rel="noopener noreferrer">github</a>
 
originally posted at https://www.devsurvival.com/todo-app-react-frontend/