Reputation: 323
I just want to understand why line 33 does not work (marked out), but line 32 does work. Why am I unable to simply push an object to the array? Shouldn't these two lines (line 32 and 33) do the exact same thing?
Reference: https://codesandbox.io/s/lingering-wood-33vtb?file=/src/App.js:0-927
import React, { useState } from "react";
function ThingsToDo({ thing }) {
return <div>{thing.text}</div>;
}
function ToDoForm({ add }) {
const [value, setValue] = useState("");
const updateKey = (e) => {
e.preventDefault();
if (!value) return;
add(value);
setValue("");
};
return (
<form onSubmit={updateKey}>
<input
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
</form>
);
}
function App() {
const [todos, setTodos] = useState([{ text: "Hi" }, { text: "Bye" }]);
const Addtolist = (text) => {
const newToDo = [...todos, { text }];
// const newToDo = todos.push({text})
setTodos(newToDo);
};
return (
<div>
{todos.map((todo, index) => (
<div>
<ThingsToDo thing={todo} />
</div>
))}
<ToDoForm add={Addtolist} />
</div>
);
}
export default App;
Upvotes: 1
Views: 2355
Reputation: 3001
1st case
That is not going to work. To trigger the state change you must use the setState
to update the state. With the todo.push({ text })
it will update the state but never going to trigger the state changes.
2nd case
push
returns the item you added to the array. So in setState you are try to override the current state array with { text : "somText" }
. So now in your render method you can't map through the state array & you are getting an Error.
3rd case
You need to understand that arrays are reference type. So even you did something like below, it is directly update the todo array since this is not a real copy.
let copy = todo;
copy.push({text});
setTodo(copy)
There are two ways that you can copy.
In Shallow Copy you only copies the values and the prototype will still refer to the original object.
In deep copy it copies everything. Even the references are copied.
In your case shallow copy is enough for perform the state update. You can do the shallow copy by [...todos, {text}]
In JavaScript if you need to do a deep copy, an easy way to do is,
const deepCpy = JSON.parse(JSON.stringiy(todos))
Also you can use deepCopy method in lodash
Upvotes: 0
Reputation: 2165
As official React document https://reactjs.org/docs/react-component.html says,
NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
You are trying to push an object inside React state array which violates the immutability.
To update the react state, you will definitely have to use setState
(in your case setTodos
since you are building functional component).
And another thing is when you push the array and assign it to a const variable, it will assign the length of the array instead of the array which is what you wanted.
As w3school page says in https://www.w3schools.com/jsref/jsref_push.asp
The push() method adds new items to the end of an array, and returns the new length.
My alternative to solving this problem is to use .concat method of Array
type because it will not mutate the original array but it will just return a new array with concatted objects instead.
Example.
setTodos(todos.concat(text));
Ref:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat
Upvotes: 0
Reputation: 596
You have to do as following,
// const newToDo = [...todos, { text }];
const newToDo = JSON.parse(JSON.stringify(todos));
newToDo.push({ text });
setTodos(newToDo);
Reasons:
Upvotes: 0
Reputation: 189
You can't mutate a state directly in react. In your 2 cases:
const newToDo = [...todos, { text }];
This one creates a copy of the todos array and adds your extra item. newTodo is a copy and then when you setTodos(newTodo) you set the state to the new copied array.
const newToDo = todos.push({text})
This one tries to push {text} into todos which is a state. You can't mutate a state directly you have to do this through setTodos().
If you are really set on using push then you can copy the state of todos first then push into the copied array but I think this is extra unecessary work.
Upvotes: 2