Todo list app
Build a todo list app using react js and localstorage
In This tutorial you will learn how to make a simple todo list app using react js.
You need to follow these steps:
1. Project Setup
- Open your terminal and navigate to your desired project directory.
- Run the following command to create a new React app named "todo-list":
npx create-react-app todo-list
- Navigate to the project directory:
cd todo-list
- Start the development server to see your initial app running in the browser:
npm start
by default on localhost:3000 you will see a welcome page.
Now let's move on the next step:
2. Working Directory
We'll work in our main component:
- App.js: The main container component.
Our folder structures would be something like this The typical folder structure for a React to-do list app would look like this:
todo-list/
├── public/ // (Optional) Stores static assets like favicon
│ └── index.html // (Optional) Serves as the entry point for the app
├── src/
│ ├── App.js // Main container component
│ └── index.js // Entry point for the React app
└── package.json // Manages project dependencies
3. Open App.js
Open App.js
and replace its content with the following code:
import React from 'react';
function App() {
return (
<div>
<h1>Todo List</h1>
<input
type="text"
placeholder="Enter a todo..."
/>
<button>Add Todo</button>
<ul>
<li>Todos will go here...</li>
</ul>
</div>
);
}
export default App;
- We Have created a very simple ui for taking input from
user
- We will use
Add Todo
button to add our todos insideli
element.
4. Add todo functionality
In App.js
and add the following code:
import React, { useState } from 'react';
function App() {
const [todos, setTodos] = useState([]);
const [inputValue, setInputValue] = useState('');
const handleAddTodo = () => {
if (inputValue.trim() !== '') {
setTodos([...todos, { title: inputValue, isCompleted: false }]);
setInputValue('');
}
};
const handleInputChange = (event) => {
setInputValue(event.target.value);
};
return (
<div>
<h1>Todo List</h1>
<input
value={inputValue}
onChange={handleInputChange}
type="text"
placeholder="Enter a todo..."
/>
<button onClick={handleAddTodo}>Add Todo</button>
<ul>
{todos.map((todo, index) => (
<li key={index}>{todo.title}</li>
))}
</ul>
</div>
);
}
export default App;
- First We Start by importing
useState
which will help us to manage our state. - Then we use
array destructuring
to manage ourtodo
which syntax is like this
const [todos, setTodos] = useState([]);
where[]
means our todos are in anarray
const [todos, setTodos] = useState([]);
- Same thing we do for
inputValue
, it will handle value from ourinput
element.
const [inputValue, setInputValue] = useState('');
- Then we create
handleInputChange
function which monitorsinput
element and set its changed value toinputValue
using settter functionsetInputValue
.
const handleInputChange = (event) => {
setInputValue(event.target.value);
};
handleAddTodo
function first check ifinputValue
is empty or not, if not then it uses setter functionsetTodos
to updatetodos
value. It then usesarray destructuring
to keep previous todo there...todos
and addingtitle
toinputValue
.
We also have isCompleted
value which we will make use later.
const handleAddTodo = () => {
if (inputValue.trim() !== '') {
setTodos([...todos, { title: inputValue, isCompleted: false }]);
setInputValue('');
}
};
5. Adding Complete functionality
Change the code inside your ul
element with following one;
<ul>
{todos.map((todo, index) => (
<li key={index}>
<input
type='checkbox'
checked={todo.isCompleted}
onChange={() => handleToggleTodoComplete(index)}
/>
{todo.title}
<button onClick={() => handleDeleteTodo(index)}>Delete</button>
</li>
))}
</ul>
- Now we are adding functionality to check whether our task completed or not.
- For this we add an input element which type is
checkbox
, and it is refering toisCompleted
, which is aboolean
value. - To handle this check functionality we create
handleToggleTodoComplete
which takesindex
as an argument every time we change checkbox value. MakehandleToggleTodoComplete
like this.
const handleToggleTodoComplete = (index) => {
const updatedTodos = todos.map((todo, i) => {
if (i === index) {
return { ...todo, isCompleted: !todo.isCompleted };
}
return todo;
});
setTodos(updatedTodos);
};
It maps over todos set isCompleted
to true in the todo
that has index
which we passed on onChange
.
6. Adding Delete functionality
Replace your code inside li
element with following code ;
<ul>
{todos.map((todo, index) => (
<li key={index}>
<input
type='checkbox'
checked={todo.isCompleted}
onChange={() => handleToggleTodoComplete(index)}
/>
{todo.title}
<button onClick={() => handleDeleteTodo(index)}>Delete</button>
</li>
))}
</ul>
- We have added a
button
element which listens for onClick event and callhandleDeleteTodo
function withindex
value. - Now Make
handleDeleteTodo
function and add following code to that
const handleDeleteTodo = (index) => {
const updatedTodos = todos.filter((_, i) => i !== index);
setTodos(updatedTodos);
};
It uses javascript filter array method to remove that perticular todo which has matching index
.
7. Add localStorage functionality
Now we are able to do add
delete
and other operations in our application but ones you refresh the page you will see all your todo has gone.
To fix this we will add localStorage
which will save our todos in browsers storage
Update App.js
below code :
import React, { useState, useEffect } from 'react';
function TodoApp() {
const [todos, setTodos] = useState([]);
const [inputValue, setInputValue] = useState('');
// Load todos from local storage on component mount
useEffect(() => {
const storedTodos = JSON.parse(localStorage.getItem('todos'));
if (storedTodos) {
setTodos(storedTodos);
}
}, []);
// Update local storage whenever todos change
useEffect(() => {
if (todos.length > 0) localStorage.setItem('todos', JSON.stringify(todos));
}, [todos]);
const handleInputChange = (event) => {
setInputValue(event.target.value);
};
const handleAddTodo = () => {
if (inputValue.trim() !== '') {
setTodos([...todos, { title: inputValue, isCompleted: false }]);
setInputValue('');
}
};
const handleDeleteTodo = (index) => {
const updatedTodos = todos.filter((_, i) => i !== index);
setTodos(updatedTodos);
// Check if there are no todos left
if (updatedTodos.length === 0) {
// Clear local storage
localStorage.removeItem('todos');
}
};
const handleToggleTodoComplete = (index) => {
const updatedTodos = todos.map((todo, i) => {
if (i === index) {
return { ...todo, isCompleted: !todo.isCompleted };
}
return todo;
});
setTodos(updatedTodos);
};
return (
<div>
<h1>Todo List</h1>
<input
type="text"
value={inputValue}
onChange={handleInputChange}`
placeholder="Enter a todo..."
/>
<button onClick={handleAddTodo}>Add Todo</button>
<ul>
{todos.map((todo, index) => (
<li key={index}>
<input
type='checkbox'
checked={todo.isCompleted}
onChange={() => handleToggleTodoComplete(ind~ex)}
/>
{todo.title}
<button onClick={() => handleDeleteTodo(index)}>Delete</button>
</li>
))}
</ul>
</div>
);
}
export default TodoApp;
- We have imported
useEffect
which will allow us to get and save ourtodos
everytime they change and when we load the page. - First we add our todo in localStorage like this
useEffect(() => {
if (todos.length > 0) localStorage.setItem('todos', JSON.stringify(todos));
}, [todos]);
We do this every time todos
change. useEffect will run everytime the value inside []
changes. That's why we have added todos
inside []
.
We are also checking todos
length it will help use when we are loading the page.
- Then add another useEffect which will run only ones the page load
useEffect(() => {
const storedTodos = JSON.parse(localStorage.getItem('todos'));
if (storedTodos) {
setTodos(storedTodos);
}
}, []);
We are also using json
to save todos in localStorage, Which converts into javascript objest seemlessly.
- Also when deleting last item we remove todos form localstorage completly by adding following code to
handleDeleteTodo
function.
if (updatedTodos.length === 0) {
localStorage.removeItem('todos');
}
Now that you've created your own todo-list app it is not end yet you can improve it by using your own css
and by adding other functionlities like updating todo
and database integration