[英]Append an object to an array, why does "push" not work?
I just want to understand why line 33 does not work (marked out), but line 32 does work.我只是想知道为什么线33不起作用(标注出来),但32行确实工作。 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?
这两行(第 32 行和第 33 行)不应该做完全相同的事情吗?
Reference: https://codesandbox.io/s/lingering-wood-33vtb?file=/src/App.js:0-927参考: 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;
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.这将创建 todos 数组的副本并添加您的额外项目。 newTodo is a copy and then when you setTodos(newTodo) you set the state to the new copied array.
newTodo 是一个副本,然后当您 setTodos(newTodo) 时,您将状态设置为新复制的数组。
const newToDo = todos.push({text})
This one tries to push {text} into todos which is a state.这个试图将 {text} 推入 todos 这是一种状态。 You can't mutate a state directly you have to do this through setTodos().
你不能直接改变状态,你必须通过 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.如果你真的开始使用推送,那么你可以先复制 todos 的状态,然后推送到复制的数组中,但我认为这是额外的不必要的工作。
You have to do as following,你必须做如下,
// const newToDo = [...todos, { text }];
const newToDo = JSON.parse(JSON.stringify(todos));
newToDo.push({ text });
setTodos(newToDo);
Reasons:原因:
As official React document https://reactjs.org/docs/react-component.html says,正如官方 React 文档https://reactjs.org/docs/react-component.html所说,
NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made.
永远不要直接改变 this.state,因为之后调用 setState() 可能会替换你所做的改变。 Treat this.state as if it were immutable.
将 this.state 视为不可变的。
You are trying to push an object inside React state array which violates the immutability.您试图将一个对象推送到 React 状态数组中,这违反了不变性。 To update the react state, you will definitely have to use
setState
(in your case setTodos
since you are building functional component).要更新反应状态,您肯定必须使用
setState
(在您的情况下是setTodos
因为您正在构建功能组件)。
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.另一件事是,当您推送数组并将其分配给 const 变量时,它将分配数组的长度而不是您想要的数组。
As w3school page says in https://www.w3schools.com/jsref/jsref_push.asp正如 w3school 页面在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.
push() 方法将新项添加到数组的末尾,并返回新长度。
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.我解决这个问题的替代方法是使用
Array
类型的.concat方法,因为它不会改变原始数组,而只会返回一个带有连接对象的新数组。
Example.例子。
setTodos(todos.concat(text));
Ref:参考:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat
1st case第一种情况
That is not going to work.那是行不通的。 To trigger the state change you must use the
setState
to update the state.要触发状态更改,您必须使用
setState
来更新状态。 With the todo.push({ text })
it will update the state but never going to trigger the state changes.使用
todo.push({ text })
它将更新状态但永远不会触发状态更改。
2nd case第二种情况
push
returns the item you added to the array. push
返回您添加到数组中的项目。 So in setState you are try to override the current state array with { text : "somText" }
.因此,在 setState 中,您尝试使用
{ 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.
所以即使你做了类似下面的事情,它也会直接更新 todo 数组,因为这不是一个真正的副本。
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}]
你可以通过
[...todos, {text}]
做浅拷贝
In JavaScript if you need to do a deep copy, an easy way to do is,在 JavaScript 中,如果您需要进行深度复制,一个简单的方法是,
const deepCpy = JSON.parse(JSON.stringiy(todos))
Also you can use deepCopy method in lodash
您也可以在
lodash
使用 deepCopy 方法
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.