Search code examples
reactjsreact-hooksstatereact-statereact-state-management

Updating a state in React leads to auto-updation of another state


I have two local states, UserList and ModifiedUsers in my App function. The UserList is supposed to capture an array of user objects from an API, and ModifiedUsers will operate on a copy of the array of objects to reverse/sort the list.

const APIURL = "https://jsonplaceholder.typicode.com/users";
function App() {
  const [UserList, setUserList] = useState([]);
  let [ModifiedUsers, setModifiedUsers] = useState([]);

This getUsers method is used for fetching data from the API and storing it in a local variable users. Which I am then passing to both the UserList array and ModifiedUsers array.

const getUsers = async () => {
    let response = await fetch(APIURL);
    let users = await response.json();
    let modifiedUsers = users;
    setUserList(users);
    setModifiedUsers(modifiedUsers);
   };

This sortList method is called on a button click to sort the array. Even though in this method I am only updating the ModifiedUsers array, it is somehow updating the UserList array as well.

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

When I console log the UserList Array, I see that it has captured the modified values of ModifiedUsers array, even though I am nowhere updating the original UserList Array.

I am unable to retain the original array of objects no matter what I try. Please help.

Here is the code live on a codepen- https://codesandbox.io/s/api-forked-ie57j4?file=/src/App.js

I have tried creating multiple states, but all of them get updated similarly. I was expecting only the ModifedUsers array to get updated, but all of them did.


Solution

  • Addition to the answer by @Janik

    This happens when the objects inside of an array or objects are still referencing the original object.

    And we should be mindful of the use of the spread operator while copying an object or array. The spread operator only makes a copy of primitive data types and not objects (makes a shallow copy of the objects). Nested objects will still be referenced to the old object.

    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"
    

    Thus, the following is only make a shallow copy.

    const modifiedUsers = [...users] // only makes shallow copy
    

    So, we should instead make a deep copy of the objects/arrays with:

    const modifiedUsers = JSON.parse(JSON.stringify(users));
    

    NB: This method can only be used with serializable objects for eg.: if your objects or array has functions then it won't work.

    More on Shallow vs Deep copy: 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.