1. The Golden Rule: State is Immutable 🏛️
Before we write any code, we must understand a core principle in React: **you should never modify state directly**. This is called "immutability".
Instead of changing the original state array (e.g., by using methods like `.push()` or `.splice()`), you must always create a **new array** with the updated data and pass that new array to your state setter function (e.g., `setTodos`).
This rule helps React track changes efficiently and prevents subtle bugs in your application.
2. Deleting an Item from State
To delete an item, we'll use the JavaScript .filter() method. This method is perfect for immutability because it **returns a new array** containing only the items that pass a certain test.
Our event handler will receive the `id` of the todo to delete. We will then filter the `todos` array, keeping every todo *except* the one with the matching `id`.
const { useState } = React;
function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Item 1', completed: false },
{ id: 2, text: 'Item 2', completed: false },
{ id: 3, text: 'Item 3', completed: false },
]);
const handleDelete = (idToDelete) => {
// Create a new array that excludes the item with the matching id
const newTodos = todos.filter(todo => todo.id !== idToDelete);
// Update the state with this new array
setTodos(newTodos);
};
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
{todo.text}
{/* We use an inline arrow function to pass the id */}
<button onClick={() => handleDelete(todo.id)}>Delete</button>
</li>
))}
</ul>
);
}
3. Updating an Item in State
To update an item (like toggling its `completed` status), we'll use the JavaScript .map() method. This method also **returns a new array**, making it ideal for immutable updates.
We will map over the original `todos` array. For each todo, we'll check if it's the one we want to update.
- If it is, we return a **new object** with the changed property.
- If it's not, we return the original, unchanged todo object.
// (Inside the same TodoList component)
const handleToggleComplete = (idToToggle) => {
const newTodos = todos.map(todo => {
// If this is the todo we're looking for...
if (todo.id === idToToggle) {
// ...return a new object with the 'completed' property flipped
return { ...todo, completed: !todo.completed };
}
// Otherwise, return the original todo
return todo;
});
setTodos(newTodos);
};