10110
10110

Reputation: 2695

React object property value being duplicated on .push inside loop

I have a handleCreate function that takes care of taking some user data and inserting it into a database.

Inside the aliasArr.forEach() loop I POST into my DB new user instances for each element in the aliasArr array. This particular code works as expected, if I check the DB, I will find the new members.

After saving the new members, I want to keep the members in the members array so I can pass it along to another function.

For this, I'm doing members.push(memberAttributes); but if I log the contents of members I get the right amount of elements but the alias property value is duplicated (all other properties should have the same value cause they are being added into the same role in a batch operation).

If I have two new users, say: xyz and abc, I get:

[
    {alias: "abc", Role: "admin", "grantedBy": "someone"},
    {alias: "abc", Role: "admin", "grantedBy": "someone"},
]

Instead of:

[
    {alias: "xyz", Role: "admin", "grantedBy": "someone"},
    {alias: "abc", Role: "admin", "grantedBy": "someone"},
]

Here's the code:

 handleCreate = () => {
    const { memberAttributes } = this.state;
    const { role, createRoleMember } = this.props;
    const roleArr = [];
    roleArr.push(role);
    const aliasArr = memberAttributes.alias.split(",");
    let members = [];

    //false hardcoded during debugging.
    if (false /* await aliasIsAdmin(memberAttributes.alias, roleArr) */) { 
      this.setState({ userExists: true }); 
    } else {
      memberAttributes["Granted By"] = window.alias;
      memberAttributes.Role = role;
      memberAttributes.timestamp = Date.now().toString();
      this.handleClose();

      aliasArr.forEach((currAlias) => {
        memberAttributes.alias = currAlias;
        console.log("memberAttributes:", memberAttributes);

        members.push(memberAttributes);
        console.log("members", members);

        const marshalledObj = AWS.DynamoDB.Converter.marshall(memberAttributes);

        const params = {
          TableName: "xxx",
          Item: marshalledObj,
        };

        axios.post(
          "https://xxx.execute-api.us-west-2.amazonaws.com/xxx/xxx",
          params
        );
      });
    }
    createRoleMember(members); //passing members to this function to do more stuff.
  };

I'm wondering if this issue is due to memberAttributes being part of the component's state.

Upvotes: 0

Views: 505

Answers (1)

Kevin Hoopes
Kevin Hoopes

Reputation: 507

The problem here is that you are pushing references to the same object into the array after changing a value within that object. So whenever you make the change to memberAttributes.alias, it's changing the alias to the most recent one. After that, all references to the same object (which in this case is every item in the members array) present the new value in alias.

const obj = { alias: 'abc', role: 'role1' }
const arr = []

arr.push(obj)

obj.alias = 'new alias'

arr.push(obj)

for (var mem of arr) {
  console.log(mem)
}

To fix it, you need to create a new object each time and push it onto the array instead, like so:

aliasArr.forEach((currAlias) => {
   // Creates a new object in memory with the same values, but overwrites alias
   const newMemberAttributes = Object.assign(memberAttributes, { alias: currAlias });
   console.log("memberAttributes:", newMemberAttributes);

   members.push(newMemberAttributes);
   console.log("members", members);
   ...
}

Similarly, you can use the spread operator to create a deep copy of the object and then reassign alias.

aliasArr.forEach((currAlias) => {
   // Creates a new object in memory with the same values, but overwrites alias
   const newMemberAttributes = { ...memberAttributes };
   newMemberAttributes.alias = currAlias
   console.log("memberAttributes:", newMemberAttributes);

   members.push(newMemberAttributes);
   console.log("members", members);
   ...
}

Upvotes: 1

Related Questions