[英]Why does reverse() work if the value of useState is an object and not if it is an array?
[英]Append an object to an array, why does "push" not work?
我只是想知道為什么線33不起作用(標注出來),但32行確實工作。 為什么我不能簡單地將對象推送到數組? 這兩行(第 32 行和第 33 行)不應該做完全相同的事情嗎?
參考: 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;
你不能在反應中直接改變狀態。 在您的兩種情況下:
const newToDo = [...todos, { text }];
這將創建 todos 數組的副本並添加您的額外項目。 newTodo 是一個副本,然后當您 setTodos(newTodo) 時,您將狀態設置為新復制的數組。
const newToDo = todos.push({text})
這個試圖將 {text} 推入 todos 這是一種狀態。 你不能直接改變狀態,你必須通過 setTodos() 來做到這一點。
如果你真的開始使用推送,那么你可以先復制 todos 的狀態,然后推送到復制的數組中,但我認為這是額外的不必要的工作。
你必須做如下,
// const newToDo = [...todos, { text }];
const newToDo = JSON.parse(JSON.stringify(todos));
newToDo.push({ text });
setTodos(newToDo);
原因:
正如官方 React 文檔https://reactjs.org/docs/react-component.html所說,
永遠不要直接改變 this.state,因為之后調用 setState() 可能會替換你所做的改變。 將 this.state 視為不可變的。
您試圖將一個對象推送到 React 狀態數組中,這違反了不變性。 要更新反應狀態,您肯定必須使用setState
(在您的情況下是setTodos
因為您正在構建功能組件)。
另一件事是,當您推送數組並將其分配給 const 變量時,它將分配數組的長度而不是您想要的數組。
正如 w3school 頁面在https://www.w3schools.com/jsref/jsref_push.asp 中所說
push() 方法將新項添加到數組的末尾,並返回新長度。
我解決這個問題的替代方法是使用Array
類型的.concat方法,因為它不會改變原始數組,而只會返回一個帶有連接對象的新數組。
例子。
setTodos(todos.concat(text));
參考:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat
第一種情況
那是行不通的。 要觸發狀態更改,您必須使用setState
來更新狀態。 使用todo.push({ text })
它將更新狀態但永遠不會觸發狀態更改。
第二種情況
push
返回您添加到數組中的項目。 因此,在 setState 中,您嘗試使用{ text : "somText" }
覆蓋當前狀態數組。 所以現在在你的渲染方法中你不能通過狀態數組進行映射,你會得到一個錯誤。
第三種情況
您需要了解數組是引用類型。 所以即使你做了類似下面的事情,它也會直接更新 todo 數組,因為這不是一個真正的副本。
let copy = todo;
copy.push({text});
setTodo(copy)
有兩種方法可以復制。
在淺拷貝中,您只復制值,原型仍將引用原始對象。
在深度復制中,它復制所有內容。 甚至參考文獻也被復制。
在您的情況下,淺拷貝足以執行狀態更新。 你可以通過[...todos, {text}]
做淺拷貝
在 JavaScript 中,如果您需要進行深度復制,一個簡單的方法是,
const deepCpy = JSON.parse(JSON.stringiy(todos))
您也可以在lodash
使用 deepCopy 方法
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.