Skip to main content

Build Simple Todo Application in React

In our previous React tutorial, we have explained how to implement Datatable in React. In this tutorial, we will explain how to build a simple Todo Application in React.

We will develop a simple Todo application to create a todo item, update it, delete
and display todo list. So we will handle complete CRUD (Create, Read, Update and Delete) operation that’s important to develop application using React.

As we will create a simple Todo list means we will create a JavaScript list to contain todos and perform operations on it. So it will not keep track of todos if we refresh page.

So let’s start build a simple TOdo Application in React.

Setup React Application

So let’s start building our todos react application. We will run command create-react-app to create react application react-todo-app.

npx create-react-app react-todo-app

we will move into our newly created application.

$ cd react-todo-app

Now we will run our react application using below command.

$ npm start

Install Bootstrap Library

For adding styling, we will install Bootstrap library using npm command.

$ npm install react-bootstrap bootstrap

Styling App

We will add some custom CSS to App.css to do some styling to todo app page. Just copy and paste this to App.css file.

.todo-app {
  padding: 30px;
  background-color: floralwhite;
}

.todo-section {
  align-items: center;
  display: flex;
  font-size: 18px;
  justify-content: space-between;
}

Implement Application

Now we will implement application main part in App.js file. We will start by importing the required files and modules in App.js file

import React from "react";
import "./App.css";
import { Button, Card, Form } from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';

We will use setTodos and will use React.useState to contain the todos details.

function App() {
  const [todos, setTodos] = React.useState([
    {
      text: "This is my test todo",
      isDone: false
    }
  ]);
}

For adding new todos, we will define a createTodo function and will define a constant newTodos which take he todos list and append the new todo’s text to the list using the spread operator. Then we will set newTodos as todos.

const createTodo = text => {
    const newTodos = [...todos, { text }];
    setTodos(newTodos);
  };
  

For marking todos as complete, we will define a completeTodo function. We will use the spread operator to copy all the todos in newTodos and then we mark the todo as complete by using its index and then set the newTodos as todos.

const completeTodo = index => {
    const newTodos = [...todos];
    newTodos[index].isDone = true;
    setTodos(newTodos);
  };
  

For deleting todos, we will define a deleteTodo function. We will use the index to splice the list to delete the todo whose index matches and then set the newTodos.

const deleteTodo = index => {
    const newTodos = [...todos];
    newTodos.splice(index, 1);
    setTodos(newTodos);
  };
  

We will complete the implementation by returning the JSX rendering to be displayed on page. We will use our already created TodoForm component and accept the createTodo as parameter. Then finally we will display all todos using the map operator. In loop, we will send the index, todo, the completeTodo and deleteTodo functions as parameter for each todos.

return (
    <div className="todo-app">
      <div className="container">
        <h1 className="text-center mb-4">Todo List</h1>
        <TodoForm createTodo={createTodo} />
        <div>
          {todos.map((todo, index) => (
            <Card>
              <Card.Body>
                <Todo
                key={index}
                index={index}
                todo={todo}
                completeTodo={completeTodo}
                deleteTodo={deleteTodo}
                />
              </Card.Body>
            </Card>
          ))}
        </div>
      </div>
    </div>
  );
  

We will define our Todo component. It accept the parameter as passed earlier for each todos. It will return JSX with buttons to complete todo and delete it.

function Todo({ todo, index, completeTodo, deleteTodo }) {
  return (
    <div className="todo-section">
      <span style={{ textDecoration: todo.isDone ? "line-through" : "" }}>{todo.text}</span>
      <div>
        <Button variant="outline-success" onClick={() => completeTodo(index)}>✓</Button>{' '}
        <Button variant="outline-danger" onClick={() => deleteTodo(index)}>✕</Button>
      </div>
    </div>
  );
}

Finally, we will define the TodoForm function which accept the createTodo as parameter to handle the functionality to create new todo.

function TodoForm({ createTodo }) {
  const [value, setValue] = React.useState("");

  const handleSubmit = e =>{
    e.preventDefault();
    if (!value) return;
    createTodo(value);
    setValue("");
  };

  return (
    <Form onSubmit={handleSubmit}>
    <div className="form-group">
      <Form.Label><b>Add Todo</b></Form.Label>
      <Form.Control type="text" className="input" value={value} onChange={e =>setValue(e.target.value)} placeholder="Add new todo" />
    </div>
	<div style={{marginTop:"10px"}}>
    <Button variant="primary mb-3" type="submit">
      Add
    </Button>
	</div>
  </Form>
  );
}

Now, let’s have the complete App.js file code :

import React from "react";
import "./App.css";
import { Button, Card, Form } from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';


function Todo({ todo, index, completeTodo, deleteTodo }) {
  return (
    <div className="todo-section">
      <span style={{ textDecoration: todo.isDone ? "line-through" : "" }}>{todo.text}</span>
      <div>
        <Button variant="outline-success" onClick={() => completeTodo(index)}>✓</Button>{' '}
        <Button variant="outline-danger" onClick={() => deleteTodo(index)}>✕</Button>
      </div>
    </div>
  );
}

function TodoForm({ createTodo }) {
  const [value, setValue] = React.useState("");

  const handleSubmit = e => {
    e.preventDefault();
    if (!value) return;
    createTodo(value);
    setValue("");
  };

  return (
    <Form onSubmit={handleSubmit}> 
    <div className="form-group">
      <Form.Label><b>Add Todo</b></Form.Label>
      <Form.Control type="text" className="input" value={value} onChange={e => setValue(e.target.value)} placeholder="Add new todo" />
    </div>
	<div style={{marginTop:"10px"}}>
    <Button variant="primary mb-3" type="submit">
      Add
    </Button>
	</div>
  </Form>
  );
}

function App() {
  const [todos, setTodos] = React.useState([
    {
      text: "This is my test todo",
      isDone: false
    }
  ]);

  const createTodo = text => {
    const newTodos = [...todos, { text }];
    setTodos(newTodos);
  };

  const completeTodo = index => {
    const newTodos = [...todos];
    newTodos[index].isDone = true;
    setTodos(newTodos);
  };

  const deleteTodo = index => {
    const newTodos = [...todos];
    newTodos.splice(index, 1);
    setTodos(newTodos);
  };

  return (
    <div className="todo-app">
      <div className="container">
        <h1 className="text-center mb-4">Todo List</h1>
        <TodoForm createTodo={createTodo} />
        <div>
          {todos.map((todo, index) => (
            <Card>
              <Card.Body>
                <Todo
                key={index}
                index={index}
                todo={todo}
                completeTodo={completeTodo}
                deleteTodo={deleteTodo}
                />
              </Card.Body>
            </Card>
          ))}
        </div>
      </div>
    </div>
  );
}

export default App;