在 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));

注意: 此方法只能用于可序列化对象,例如:如果您的对象或数组具有函数,那么它将不起作用。

关于浅拷贝与深拷贝的更多信息:https://www.freecodecamp.org/news/copying-stuff-in-javascript-how-to-differentiate-between-deep-and-shallow-copies-b6d8c1ef09cd/#:~:text=A%20deep%20copy%20means%20that,into%20how%20JavaScript%20stores%20values