I'm having trouble understanding why, when there are objects inside the vector, it doesn't create new references to them when copying the vector.
But here's the problem.
const USERS_TYPE = [
{name:"User",icon:faTruck,
inputs:[
{label:"Name",required:true,width:"200px",value:"", error:false},
{label:"ID",required:true,width:"150px",value:"", error:false},
{label:"Email",required:false,width:"150px",value:"", error:false},
]}]
I tried to pass this vector to a state in two ways.
const [users,setUsers] = useState(USERS_TYPE.map(item=>({...item})))
const [users,setUsers] = useState([...USERS_TYPE])
And in both situations changing user with setUser will change USERS_TYPE.
One of the ways I change.
const changes = [...users]
const err = validation(changes[selected-1].inputs)
err.map((index)=>{
changes[selected-1].inputs[index].error = true
})
setUsers(changes)
What solutions could I come up with, change from vector to object, another copy mechanism. This copy doesn't make much sense as the internal object references remain intact.
Edit: Another important detail is that the USER_TYPE is outside the function component.
it doesn't create new references to them when copying the vector
Because that's not the way JS works. It doesn't deep clone stuff automagically.
const user1 = {id:1}
const user2 = {id:2}
const users = [user1,user2];
const newUsers = [...users]; // this clones users, BUT NOT the objects it contains
console.log(newUsers === users); // false, it's a new array
console.log(newUsers[0] === users[0]) // true, it's the same reference
Ultimately, you are just mutating state. First and most golden rule of react: don't mutate state.
This is the line causing the error:
err.map((index)=>{
// you are mutating an object by doing this
// yes, `changes` is a new array, but you are still mutating the object that is part of state that is nested inside of that array
changes[selected-1].inputs[index].error = true
})
Maybe this would work:
const idx = selected-1;
const err = validation(users[idx].inputs)
setUsers(users => users.map((user,i) => {
if(i !== idx) return user; // you aren't interested in this user, leave it unmodified
// you need to change the inputs for this user
// first, shallow clone the object using the spread operator
return {
...user,
// now inputs must be a new reference as well, so clone it using map
inputs: user.inputs.map((input,index) => ({
// careful not to mutate the input object, clone it using spread
...input,
// and set the error property on the cloned object
error: !!err[index]
}))
}
}))
EDIT: Sorry for all the code edits, I had a bunch of syntax errors. The ultimate point I was trying to get across remained consistent.
EDIT #2:
Another important detail is that the USER_TYPE is outside the function component.
That doesn't really matter in this case as it serves as your initial state. Every time you update state you need to do it immutably (as I've shown you above) so as not to mutate this global object. If you actually mutate it, you'll see that by unmounting the component and re-mounting the component will result in what looks like "retained state" - but it's just that you mutated the global object that served as the template for initial state.