0

New to React.

I was trying to update the boolean value 'done' whenever the user clicks on it.

For some reasons, this code doesn't work

var updateTodo = todos;
updateTodo[Number(id)].done = !updateTodo[Number(id)].done;
setTodos(updateTodo);

but this does

var updateTodo = [...todos];
updateTodo[Number(id)].done = !updateTodo[Number(id)].done;
setTodos(updateTodo);

As far as I think todos and [...todos] both return the same values i.e. todos array, then why won't the first code snippet work?

Here is working App.tsx for reference, not working snippet present in the function handleDone.

import React from "react";

type item = {
  itemId: number;
  todo: string;
  done: boolean;
};

var id = 0;

const App: React.FC = () => {
  const [newTodo, setNewTodo] = React.useState<string>("");
  const [todos, setTodos] = React.useState<item[]>([]);

  const handleChange = (event: any) => {
    const { value } = event.target;
    setNewTodo(value);
  };

  const handleSubmit = (event: any) => {
    setTodos([...todos, { itemId: id, todo: newTodo, done: false }]);
    id++;
    setNewTodo("");
    event.preventDefault();
  };

  const handleDone = (item: any) => {
    // Not Working
    var updateTodo = todos;
    updateTodo[Number(id)].done = !updateTodo[Number(id)].done;
    setTodos(updateTodo);

    // Working Code
    // var updateTodo = [...todos];
    // updateTodo[Number(id)].done = !updateTodo[Number(id)].done;
    // setTodos(updateTodo);
  };

  return (
    <>
      <form>
        <input onChange={handleChange} value={newTodo} type="text" name="todo" placeholder="Enter Todo" />
        <button onClick={handleSubmit}>Add</button>
      </form>
      <ul>
        {todos.map((item, index) => {
          return item.done ? (
            <li id={`${item.itemId}`} onClick={handleDone} style={{ textDecoration: "line-through" }}>
              {item.todo}
            </li>
          ) : (
            <li id={`${item.itemId}`} onClick={handleDone} style={{ textDecoration: "none" }}>
              {item.todo}
            </li>
          );
        })}
      </ul>
    </>
  );
};

export default App;

Miraaj Kadam
  • 59
  • 2
  • 4
  • 3
    `foo = arr`, then `foo` evaluates to the *same array object* as `arr`. `foo = [...arr]`, then foo is a *new array* created via a *shallow copy* and changing `foo` (eg. adding or removing an element) does not affect `arr`. Returning or passing a value has the *the same behavior in this regard as an assignment* (that is, no copy/clone is created implicitly). Sometimes both are ok. Sometimes using the same object is necessary. Sometimes a copy is required for proper behavior. It really depends on what later modifies the array (or arrays) and what the expectations are. – user2864740 Apr 13 '21 at 17:56
  • [What Do You Mean “It Doesn’t Work”?](https://meta.stackexchange.com/q/147616/289905) Most likely duplicate of [Modifying a copy of a JavaScript object is causing the original object to change](https://stackoverflow.com/q/29050004/4642212). – Sebastian Simon Apr 13 '21 at 21:48
  • To reiterate, both of these are mutations of state and neither is ok. But the second case causes a re-render because `updateTodo` is a different array whereas in the first case you are setting the state to itself. – Linda Paiste Apr 14 '21 at 00:29

0 Answers0