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 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.
CodePudding user response:
Essentially, you are modifying your UserList
— not only because it obviously behaves the same like your ModifiedUsers
but because of the way you created it.
In JavaScript, all object variables (meaning everything except for primitives like string and boolean) are stored and copied by reference. This means: the value of users
is only a pointer, pointing to the location of your actual array. When you copy this into your ModifiedUsers
, you are only copying the pointer, which in turn leads to using the same array. And by sorting this one, the same change (hence the same array) is visible in UserList
.
To modify the two arrays independently, you have to clone them, in order to create an exact copy with new pointers. For example, you can use the JavaScript spread syntax:
let modifiedUsers = [...users]
Keep in mind that this will only clone 1 level, so all the object pointers inside the array remain the same. If you need to change them independently as well, you have to deep clone your array. You can find some more inspiration on this here or on the web.
CodePudding user response:
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 deep copy means that,into how JavaScript stores values.