Create a Todo List Application in React (add, delete, and edit items)
Last updated: December 23, 2023.
In this tutorial, we will create a todo list application in React that initially displays an array to todos as string values.
Then we will look at how to add, delete and edit todos.
Display todo list items
Todo list items can be displayed in a list by mapping over them using the map()
function.
So that the UI will update whenever todos are changed later, the todos item are stored as a state value:
import { useState } from "react";
const App = () => {
const initialTodos = ["Code", "Sleep", "Eat"];
const [ todos, setTodos ] = useState(initialTodos);
return (
<div>
<ul>
{
todos.map((todo, index) => {
return <li key={index}>{ todo }</li>
})
}
</ul>
</div>
);
};
export default App;
Adding todos
To add todos to the list, you can add an <input>
element that, when a user types, update the state value of todoInput
using setTodoInput
.
When the <button>
below it is clicked, this triggers an updating of todos
using setTodos
, the new value of which is the previous array plus the new string value of todoInput
:
import { useState } from "react";
const App = () => {
const initialTodos = ["Code", "Sleep", "Eat"];
const [ todos, setTodos ] = useState(initialTodos);
const [ todoInput, setTodoInput ] = useState('');
return (
<div>
<ul>
{
todos.map((todo, index) => {
return <li key={index}>{ todo }</li>
})
}
</ul>
<div>
<input
type="text"
onChange={ (e) => { setTodoInput(e.target.value) } }
value={todoInput}
/>
<button onClick={ () => { setTodos(prev => [...prev, todoInput]) } }>Add todo</button>
</div>
</div>
);
};
export default App;
Deleting todos
To be able to delete a todo item, a button that displays "Delete"
can be added next to each todo item.
When it is clicked, the new todo items are set to a filtered version of the current items minus the todo item that matches the value of index
:
import { useState } from "react";
const App = () => {
const initialTodos = ["Code", "Sleep", "Eat"];
const [ todos, setTodos ] = useState(initialTodos);
const [ todoInput, setTodoInput ] = useState('');
return (
<div>
<ul>
{
todos.map((todo, index) => {
return(
<li key={index}>
{ todo }
<button onClick={ () => { setTodos(prev => prev.filter((todo, i) => i !== index)) } }>Delete</button>
</li>
)
})
}
</ul>
<div>
<input
type="text"
onChange={ (e) => { setTodoInput(e.target.value) } }
value={todoInput}
/>
<button onClick={ () => { setTodos(prev => [...prev, todoInput]) } }>Add todo</button>
</div>
</div>
);
};
export default App;
Editing todos
To edit todo items, we can create a <button>
that display "Edit"
next to each todo item.
Clicking this will cause the state value editingTodo
to update to the index of the item clicked, which leads to conditional rendering of the item’s current value in an <input>
element. Its current value is set as the state value editingTodoInput
which is also set when the "Edit"
button is clicked.
Then, by clicking the <button>
displaying "Done editing"
next to the <input>
that is visible when editing, todos
is updated with the new value set for the edited item.
Finally, the value of editingTodo
is set to false
and this closes the editing view.
import { useState } from "react";
const App = () => {
const initialTodos = ["Code", "Sleep", "Eat"];
const [ todos, setTodos ] = useState(initialTodos);
const [ todoInput, setTodoInput ] = useState('');
const [ editingTodo, setEditingTodo ] = useState(false);
const [ editingTodoInput, setEditingTodoInput ] = useState(null);
return (
<div>
<ul>
{
todos.map((todo, index) => {
return(
<li key={index}>
{ (editingTodo === index) ?
<>
<input value={ editingTodoInput } onChange={ (e) => { setEditingTodoInput(e.target.value) } }/>
<button
onClick={ () => { setTodos(prev => { prev[index] = editingTodoInput; return prev; }); setEditingTodo(false) } }
>
Done editing
</button>
</>
: todo
}
<button onClick={ () => { setEditingTodoInput(todos[index]); setEditingTodo(index); } }>Edit</button>
<button onClick={ () => { setTodos(prev => prev.filter((todo, i) => i !== index)) } }>Delete</button>
</li>
)
})
}
</ul>
<div>
<input
type="text"
onChange={ (e) => { setTodoInput(e.target.value) } }
value={todoInput}
/>
<button onClick={ () => { setTodos(prev => [...prev, todoInput]) } }>Add todo</button>
</div>
</div>
);
};
export default App;