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;