Reputation: 137
I have an array of objects in todos state hook. And I want to change a single property if it is completed. Seems I can update it but without using setTodos. I am a React beginner.
const [todos, setTodos] = useState([]);
const [input, setInput] = useState("");
const addTodoHandler = (e) => {
e.preventDefault();
if (input.length < 2) return;
setTodos([...todos, { id: Date.now(), text: input, isComplete: false }]);
setInput("");
};
const removeHandler = (id) => {
setTodos(todos.filter((todo) => todo.id !== id));
};
const completeHandler = (id) => {
// working without setTodo()
// errors when added setTodo()
todos.map((todo) =>
todo.id === id ? console.log((todo.isComplete = !todo.isComplete)) : ""
);
};
<div className="todolist">
{todos.map((todo) => (
<Todo
key={todo.id}
id={todo.id}
text={todo.text}
removeHandler={removeHandler}
completeHandler={completeHandler}
isComplete={todo.isComplete}
/>
))}
</div>
Upvotes: 2
Views: 4929
Reputation: 73906
To fix this inside completeHandler()
first create a new array using map()
method and inside map()
method update the isComplete
for the current todo
and simply return the updated value like:
var updatedTodos = todos.map((todo) => todo.id === id ? {
...todo,
isComplete: !todo.isComplete
} : todo);
Then inside setTodos()
just return this new updatedTodos
array like:
setTodos(updatedTodos);
You can also do this in one-line like:
setTodos(todos.map((todo) => todo.id === id ? { ...todo, isComplete: !todo.isComplete } : todo));
But the previous code provides more readability and also helps in better debugging if you want to check each variable line by line.
Upvotes: 7