在 React 中更新一个状态会导致另一个状态的自动更新
Updating a state in React leads to auto-updation of another state
我的 App 函数中有两个本地状态,UserList 和 ModifiedUsers。 UserList 应该从 API 中捕获用户对象数组,而 ModifiedUsers 将对对象数组的副本进行操作以 reverse/sort 列表。
const APIURL = "https://jsonplaceholder.typicode.com/users";
function App() {
const [UserList, setUserList] = useState([]);
let [ModifiedUsers, setModifiedUsers] = useState([]);
此 getUsers 方法用于从 API 获取数据并将其存储在局部变量 users 中。然后我将其传递给 UserList 数组和 ModifiedUsers 数组。
const getUsers = async () => {
let response = await fetch(APIURL);
let users = await response.json();
let modifiedUsers = users;
setUserList(users);
setModifiedUsers(modifiedUsers);
};
在单击按钮对数组进行排序时调用此 sortList 方法。尽管在这种方法中我只更新了 ModifiedUsers 数组,但它也在以某种方式更新了 UserList 数组。
const sortList = () => {
let temp = 0;
for (let i = 0; i < ModifiedUsers.length - 1; i++) {
if (
ModifiedUsers[i].name.trim().length >
ModifiedUsers[i + 1].name.trim().length
) {
temp = ModifiedUsers[i];
ModifiedUsers[i] = ModifiedUsers[i + 1];
ModifiedUsers[i + 1] = temp;
}
}
setModifiedUsers(ModifiedUsers);
if (click % 2 === 0) {
ModifiedUsers.reverse();
setModifiedUsers(ModifiedUsers);
}
setClick(++click);
};
当我在控制台记录 UserList 数组时,我看到它已经捕获了 ModifiedUsers 数组的修改值,即使我没有更新原始 UserList 数组。
无论我怎样尝试,我都无法保留原来的对象数组。请帮忙。
这是代码笔上的实时代码- https://codesandbox.io/s/api-forked-ie57j4?file=/src/App.js
我试过创建多个状态,但所有状态都以类似方式更新。我只希望 ModifedUsers 数组得到更新,但他们都做到了。
本质上,您 是 修改您的 UserList
— 不仅因为它的行为显然与您的 ModifiedUsers
相同,还因为您创建它的方式.
在 JavaScript 中,所有对象变量(表示除了字符串和布尔值等基本类型之外的所有内容)都是 stored and copied by reference。这意味着:users
的值只是一个指针,指向实际数组的位置。当您将其复制到 ModifiedUsers
中时,您只是在复制指针,这反过来会导致使用相同的数组。通过对这个进行排序,在 UserList
.
中可以看到相同的变化(因此是相同的数组)
要独立修改这两个数组,您必须克隆它们,以便创建具有新指针的精确副本。例如,您可以使用 JavaScript spread syntax:
let modifiedUsers = [...users]
请记住,这只会克隆 1 级,因此数组中的所有对象指针都保持不变。 (它只制作浅表副本,正如@BikramKarki 的回答中所指出的那样)如果您还需要独立更改它们,则必须深度克隆您的数组。您可以在此 here 或网络上找到更多灵感。
@Janik 对答案的补充
当一个或多个数组内部的对象仍在引用原始对象时,会发生这种情况。
并且在复制对象或数组时,我们应该注意展开运算符的使用。传播运算符只复制原始数据类型而不是对象(制作对象的浅表副本)。嵌套对象仍将引用旧对象。
const personA = {
name: "A",
age: 5,
address: {
city: "City A",
country: "Country A"
}
}
const personB = {...personA};
personB.name = "B";
personB.address.city = "City B"
console.log(personA.name); // "A"
console.log(personA.address.city); // "City B" cause address object in "personB" is still referencing the address object in "personA"
所以下面只是浅拷贝
const modifiedUsers = [...users] // only makes shallow copy
因此,我们应该使用以下方法对 objects/arrays 进行深度复制:
const modifiedUsers = JSON.parse(JSON.stringify(users));
注意: 此方法只能用于可序列化对象,例如:如果您的对象或数组具有函数,那么它将不起作用。
我的 App 函数中有两个本地状态,UserList 和 ModifiedUsers。 UserList 应该从 API 中捕获用户对象数组,而 ModifiedUsers 将对对象数组的副本进行操作以 reverse/sort 列表。
const APIURL = "https://jsonplaceholder.typicode.com/users";
function App() {
const [UserList, setUserList] = useState([]);
let [ModifiedUsers, setModifiedUsers] = useState([]);
此 getUsers 方法用于从 API 获取数据并将其存储在局部变量 users 中。然后我将其传递给 UserList 数组和 ModifiedUsers 数组。
const getUsers = async () => {
let response = await fetch(APIURL);
let users = await response.json();
let modifiedUsers = users;
setUserList(users);
setModifiedUsers(modifiedUsers);
};
在单击按钮对数组进行排序时调用此 sortList 方法。尽管在这种方法中我只更新了 ModifiedUsers 数组,但它也在以某种方式更新了 UserList 数组。
const sortList = () => {
let temp = 0;
for (let i = 0; i < ModifiedUsers.length - 1; i++) {
if (
ModifiedUsers[i].name.trim().length >
ModifiedUsers[i + 1].name.trim().length
) {
temp = ModifiedUsers[i];
ModifiedUsers[i] = ModifiedUsers[i + 1];
ModifiedUsers[i + 1] = temp;
}
}
setModifiedUsers(ModifiedUsers);
if (click % 2 === 0) {
ModifiedUsers.reverse();
setModifiedUsers(ModifiedUsers);
}
setClick(++click);
};
当我在控制台记录 UserList 数组时,我看到它已经捕获了 ModifiedUsers 数组的修改值,即使我没有更新原始 UserList 数组。
无论我怎样尝试,我都无法保留原来的对象数组。请帮忙。
这是代码笔上的实时代码- https://codesandbox.io/s/api-forked-ie57j4?file=/src/App.js
我试过创建多个状态,但所有状态都以类似方式更新。我只希望 ModifedUsers 数组得到更新,但他们都做到了。
本质上,您 是 修改您的 UserList
— 不仅因为它的行为显然与您的 ModifiedUsers
相同,还因为您创建它的方式.
在 JavaScript 中,所有对象变量(表示除了字符串和布尔值等基本类型之外的所有内容)都是 stored and copied by reference。这意味着:users
的值只是一个指针,指向实际数组的位置。当您将其复制到 ModifiedUsers
中时,您只是在复制指针,这反过来会导致使用相同的数组。通过对这个进行排序,在 UserList
.
要独立修改这两个数组,您必须克隆它们,以便创建具有新指针的精确副本。例如,您可以使用 JavaScript spread syntax:
let modifiedUsers = [...users]
请记住,这只会克隆 1 级,因此数组中的所有对象指针都保持不变。 (它只制作浅表副本,正如@BikramKarki 的回答中所指出的那样)如果您还需要独立更改它们,则必须深度克隆您的数组。您可以在此 here 或网络上找到更多灵感。
@Janik 对答案的补充
当一个或多个数组内部的对象仍在引用原始对象时,会发生这种情况。
并且在复制对象或数组时,我们应该注意展开运算符的使用。传播运算符只复制原始数据类型而不是对象(制作对象的浅表副本)。嵌套对象仍将引用旧对象。
const personA = {
name: "A",
age: 5,
address: {
city: "City A",
country: "Country A"
}
}
const personB = {...personA};
personB.name = "B";
personB.address.city = "City B"
console.log(personA.name); // "A"
console.log(personA.address.city); // "City B" cause address object in "personB" is still referencing the address object in "personA"
所以下面只是浅拷贝
const modifiedUsers = [...users] // only makes shallow copy
因此,我们应该使用以下方法对 objects/arrays 进行深度复制:
const modifiedUsers = JSON.parse(JSON.stringify(users));
注意: 此方法只能用于可序列化对象,例如:如果您的对象或数组具有函数,那么它将不起作用。