2

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.

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.