Creating a Simple Todo App with React🖥️
Tags: react, tutorial, javascript, github
Published: 2023-03-16 12:08:03

A todo app is one of the simplest applications you can create, but it can still teach you a lot about React. In this post, we will build a basic todo app with React using functional components, hooks, and state management. The Project consists of 5 sections (6 if you count the file preparation) and in the end of each section I have provided you with a link to a GitHub repo with different branches.Eaxh branch represents every step along the way. The files we'll be working with are located in the **src** folder.

At any point you can refer to my code on GitHub, or navigate directly to the master branch to check out the entire project 😉

## 0.  React set up and file preparation:

Let's start by creating our app using the cra(create-react-app) tool. In the terminal run `npx create-react-app react-todo-list`. Then `cd react-todo-list` and last `npm start`. More information can be found on react official page

https://beta.reactjs.org/learn/start-a-new-react-project

Continue with cleaning up the **App.js** and **Index.js** files. they should look like this:
```
// ----------- App.js file ---------------------------
import React, { Component } from 'react'
import './App.css';

export default class App extends Component {
  render() {
    return (
      <div>
        <App/>
      </div>
    )
  }
}
```

```
// ------------ index.js file ---------------------------
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));
```

Make a **TodoList.js** file with a `ul` element. Add  Constructor(props) to manage State in the file. At this point its an empty array:
```
this.state = { todos: [] } 
```
Make a **todo.js** component that returns a `div` with an `<li>{this.props.task}<li>` and 2 buttons for “edit” and “delete”. The ‘task’ will be the argument that we are going to work with inside every **Todo** component we are rendering. It can be named anything  e.g. YellowNutCracker, but its a good practise to name our variables and files according to their role/function.
Import the **Todo.js** file into **TodoList.js** and add sample data in the state, passing data as JSX:
```
`this.state = {todos:[task:”Feed the cat”]}`
```
Now its time to access the data (each task in `Todo` component) from our `state` and push it to the array. I like to use the `array.map()` method for this that **creates a new array** populated with the results of calling a provided function on every element in the calling array. Then we ‘ll return a **Todo React Component** with a task `property`:
```
const todos = this.state.todos.map(t ⇒ { 
return <Todo task={t.task} />; 
});
```
Shortly after, render  the `{todos}` array inside our `<ul>` element. Use JSX syntax (duh!)
```
<ul> {todos} </ul>
```
At this point your code should look like this:
https://github.com/Harry2gks/react-todo-list-v2/tree/0.-Setup/src

## 1. Building the Form

We want the user to add, edit and delete his todos from our app. For that we can build a `<form>` to accept the user inputs. Lets start by creating the Form.js file. Type rcc to generate the basic template if you have the extension enabled in Vs code, otherwise I reccoment to enable the extension to make your life easier. Create a Constructor(props) function to control the state in this Component and set task as an empty string, since it will accept the user input as a string
```
this.state = {task: “”}`
`<form>
<label htmlFor=’task’> </label>
<input 
type=”text” 
placeholder=”New Todo” 
id=”task” 
name="task"
value={this.state.task} 
onChange = {this.handleChange} // controls what the user can change
/>`
`<button>Add Todo</button>
</form>
```
Now it is time for our best friend `handleChange()` method. Note that this is a custom method, not a standart JS method, like `array.map()`
```
`handleChange(evt) {
  this.setState({
    [evt.target.name] : evt.target.value // from the “name” : change the “value”
});`
```
and don’t forget to bind our method to the component in the Constructor:
```
this.handleChange = this.handleChange.bind(this);
```

### 2. Adding new Todo / Create functionality

Let’s add the Create functionality from C.R.U.D. .For that we are going to make a new method `create()` that is going to accept an object as a parameter and push it in `[todos]` array inside our state. Thats why we are going to build it inside our **TodoList.js** file:
```
create(newTodo) {
  this.setState({
    todos: […this.state.todos, newTodo] // Take the array from state and add   newTodo object
  });
}
```
 and bind it:
```
this.create = this.create.bind(this);
```
Then we need to pass our method as a property inside the `<Form>`  we are rendering here, in our **TodoList.js** file
```
<Form createTodo={this.create} />
```
Now we have the method `create()` inside our **Form.js** file as prop called createTodo, and will call it inside the `handleSubmit()`, passing the state as parameter. The `handleSubmit()` will be called when the user submits the form, thus creating a new `<Todo>` and will push the user input as a task to the state. Then we’ll clear the form:
```
`handleSubmit(evt) {
  evt.preventDefault();
  this.props.createTodo(this.state);
  this.setState({ task:”” })
}`
```
and bind it:
```
this.handleSubmit = this.handleSubmit.bind()this;
```
At this point the code should look like this:
[https://github.com/Harry2gks/react-todo-list-v2/tree/C-from-C.R.U.D](https://github.com/Harry2gks/react-todo-list-v2/tree/C-from-C.R.U.D)

## 3. Adding the Delete functionallity

Deleting is similar to creating, we’will make a `delete()` function inside our **TodoList.js** file using the `array.filter()` method. We ‘ll also pass it as a prop inside our `render()`, so that we’ll be able to call it inside the `handleDelete()` function in our **Todo.js** file. At this point I would like to point out that every `<Todo>` we’ re rendering requires a react `{key}` and also an `{id}` since we'll be deleting using the id. I like to use a library that helps me create different ids for every item called uuid : [https://www.npmjs.com/package/uuid]
(https://www.npmjs.com/package/uuid)
```
`delete(id) {
    this.setState({
      todos: this.state.todos.filter((t) => t.id !==id),
    });
  }`

`<Todo 
      key={t.id} 
      id={t.id} 
      task={t.task} 
      deleteTodo={this.delete} // delete() passed as a prop inside render()
      />;`

`handleDelete() {
      this.props.deleteTodo(this.props.id)
    }`
```
and bind it. 

Now let’s add the `handleDelete()` to our button:
```
<button onClick={this.handleDelete}>Delete</button>
```
You code should look like this now:
[https://github.com/Harry2gks/react-todo-list-v2/tree/D-from-C.R.U.D-/src](https://github.com/Harry2gks/react-todo-list-v2/tree/D-from-C.R.U.D-/src)

## 4.  Editing / Updating Todo

This part is a bit tricky. In order to edit the <Todo> we’ll need to add some conditinal logic in our **Todo.js** file to display the editing form when the user is editing or the list of todos when the user is not.
In our state add the boolean logic `isEditing: false` and borrow the task from the todo:
```
this.state = {
  isEditing: false,
  task: this.props.task
}
```
We’ll also make a custom method `toggleForm()` that is going to display or hide the form as mentioned above. And of course we ‘ll need to create a form:
```
render() {
  let edit;
  if (this.state.isEditing) {
    edit = (
      <form onSubmit={this.handleSubmit}>
        <input 
        type="text"
        name="task"
        value={this.state.task}
        onChange={this.handleChange}/>
        <button>Update</button>
      </form>
    );
  } else {
    edit = (
    <div>
      <button onClick={this.toggleForm}{>Edit</button>
      <button onClick={this.handleRemove}>Delete</button>
    </div>
    )
  }
  return edit;
}
```
As we’ve seen before we need our best friends `handleChange()` and `handleSubmit()` just like in the **Form.js** file and our `toggleForm()` connected with an `onClick` event to our button.

```
handleChange(evt) {
  this.setState({
    [evt.target.name]: evt.target.value
  });
}
handleSubmit(evt) {
  evt.preventDefault();
  // take the new task Data and pass it to the parent (todoList)
  this.props.updateTodo(this.props.id, this.state.task);
  this.setState({ isEditing: false });
}
toggleForm() {
  this.setState({ isEditing: !this.state.isEditing });
}
```
and bind in `Constructor`. I know, I Know.

Next we need to take the updated task and pass it up to the parent component **TodoList.js.**  To do that we’ll need to define a method and know 2 things: what are we updating -->id<-- and the new task -->updatedTodo<--:
```
update(id, updatedTask) {
    const updatedTodos = this.state.todos.map((todo) => {
      if (todo.id === id) {
        return { ...todo, task: updatedTask };
      }
      return todo;
    });
    this.setState({ todos: updatedTodos });
  }
      return todo;
    });
    this.setState({ todos: updatedTodos });
  }
```

and pass the methods to our render inside the <Todo>

```
<Todo 
    key={t.id} 
    id={t.id} 
    task={t.task} 
    removeTodo={this.remove}
    updateTodo={this.update}
    />
```

at this point your code should look like this:

[https://github.com/Harry2gks/react-todo-list-v2/tree/U-from-C.R.U.D-](https://github.com/Harry2gks/react-todo-list-v2/tree/U-from-C.R.U.D-)

Section 5 --> Mark Todo as “completed”

Lets make sure our Todos can keep track of when they are completed or not. In our **Form.js** file, when we make a new todo (`createNewTodo`) we’ll add `completed:false` boolean logic to handleSubmit method:
```
handleSubmit(evt) {
    evt.preventDefault();
    this.props.createNewTodo({ ...this.state, id: uuidv4(), completed: false });
    this.setState({ task: "" });
  }
```

Then add a conditional className in our `<li>` that displays the task inside the Todo.js:
`<li className={this.props.completed ? "completed" : ""}>`

and create a `.completed` class in our **App.css**:

```
.completed { 
    colour: gray, 
    text-decoration: line-through 
}
```
and pass it down in our TodoList when we render the Todo component

```
render() {
    const todos = this.state.todos.map((t) => {
      return <Todo 
      key={t.id} 
      id={t.id} 
      task={t.task} 
      completed={t.completed} // <------------ 
      removeTodo={this.remove}
      updateTodo={this.update}
      />;
    });
```

Following the same pattern with **edit**, we are going to add a button that toggles the form, calls a method in the parent element and updates the state. In our **TodoList.js** file:

```
toggleCompleted(id) {
    const updatedTodos = this.state.todos.map((t) => {
      if (t.id === id) {
        return { ...t, completed: !t.completed };
      }
      return t;
    });
    this.setState({ todos: updatedTodos });

this.toggleCompleted = this.toggleCompleted.bind(this); // up top
```

and then pass it down as a prop to our **<Todo>:**
`toggleTodo={this.toggleCompleted}`.

Since we’re making the `<li>` clickable, then we ‘ll require an onClick event and our `handleChange()` method, with a different name `handleToggle().` in **Todo.js** file:

```
<li className={this.props.completed ? "completed" : ""}
          onClick={this.handleToggle}>
            {this.props.task}</li>

handleToggle(evt) {
    this.props.toggleTodo(this.props.id);
  }

this.handleToggle = this.handleToggle.bind(this); // up top
```

Code up to here:
[https://github.com/Harry2gks/react-todo-list-v2/tree/Toggle-Completed-Todos/src](https://github.com/Harry2gks/react-todo-list-v2/tree/Toggle-Completed-Todos/src)

This article is starting to get a bit long, so i am gonna wrap it up with some styling.
Give the <div> element inside our App.js file
className=”container” and add the following styles into App.css, or copy my stylesheet directly from github.

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

.container {
    display: block;
    width: 500px;
    margin: 10px auto 100px;
    background-color: #cc9797;
    padding: 0px 10px 10px 10px;
    border-radius: 10px;
    text-align: center;
    color: #7b0e0e;
    font-size: 18px;
  }

ul {
    list-style: none;
}

li {
    margin: 10px;
    font-size: 22px;
}

li:hover {
    cursor: pointer;
}

ul button {
    padding: 2px 2px 2px 2px;
    margin: 2px 4px 2px 4px;
    font-size: 18px;
}

button {
    background: #7b0e0e;
    background-image: -webkit-linear-gradient(top, #7b0e0e, #7a0e40);
    background-image: -moz-linear-gradient(top, #7b0e0e, #7a0e40);
    background-image: -ms-linear-gradient(top, #7b0e0e, #7a0e40);
    background-image: -o-linear-gradient(top, #7b0e0e, #7a0e40);
    background-image: linear-gradient(to bottom, #7b0e0e, #7a0e40);
    -webkit-border-radius: 8;
    -moz-border-radius: 8;
    border-radius: 8px;
    -webkit-box-shadow: 0px 1px 3px #524d52;
    -moz-box-shadow: 0px 1px 3px #524d52;
    box-shadow: 0px 1px 3px #524d52;
    font-family: Arial;
    color: #ebbcbc;
    font-size: 20px;
    padding: 4px 4px 4px 4px;
    text-decoration: none;
}

button:hover {
    background: #c91818;
    background-image: -webkit-linear-gradient(top, #c91818, #610c34);
    background-image: -moz-linear-gradient(top, #c91818, #610c34);
    background-image: -ms-linear-gradient(top, #c91818, #610c34);
    background-image: -o-linear-gradient(top, #c91818, #610c34);
    background-image: linear-gradient(to bottom, #c91818, #610c34);
    text-decoration: none;
}

input {
    padding: 5px;
    margin: 0px 20px;
    border-radius: 8px;
    background-color: #e7bcbc;
    color: #7b0e0e;
}
```
Our lovely project is done! (finally)

Critisism accepted and appreciated :)

Live Project: https://react-todo-list-v2-lemon.vercel.app/
GitHub https://github.com/Harry2gks