2

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>
halfer
  • 19,824
  • 17
  • 99
  • 186
Chadric Gotis
  • 117
  • 1
  • 13
  • Does this answer your question? [How to use \`setState\` callback on react hooks](https://stackoverflow.com/questions/56247433/how-to-use-setstate-callback-on-react-hooks) – Utsav Patel May 09 '20 at 06:28
  • Can you share the error log? – Trí Phan May 09 '20 at 06:29
  • `TypeError: Cannot read property 'id' of undefined 47 |
    48 | {todos.map((todo) => ( 49 | 50 | key={todo.id} | ^ 51 | id={todo.id} 52 | text={todo.text} 53 | removeHandler={removeHandler} ` when added setTodos() :(
    – Chadric Gotis May 09 '20 at 06:32

1 Answers1

6

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.

palaѕн
  • 72,112
  • 17
  • 116
  • 136