2

I'm trying to modify a property value of a specific index in my state this property is the post_comments But my problem is the state is being modified even though i am only modifying the copy of it.. The code works how i want it to be but i'm modifying the state so it's probably bad how do i fix this?

socket.on('statusComment', (data) => {
    const mutator =  Object.assign([], this.state.getStatus);
    const index =  mutator.findIndex(i => i._id === data._id);
    mutator[index].post_comments = data.post_comments; // Replace old post_comments with data.post_comments 
    console.log(mutator) // Got the post_comments 
    console.log(this.state.getStatus) // Also modified 

    // Commented out setState
    // this.setState({
    //     getStatus: mutator
    // })
});

Here is a sample data detected by socket

const data = {
  post_id: "5b0689f03fb2fd1404f1854d",
  post_comments: [{text: 'what'}]
}

This is what my state looks like

   const data_arr = [
      {
        "post_img": [],
        "post_date": "2018-05-24T09:46:24.948Z",
        "post_comments": [
          {
            "comment_posted": "2018-05-24T09:46:31.015Z",
            "_id": "5b0689f73fb2fd1404f1854e",
            "comment_from": {
              "photo_url": "png",
              "_id": "5af16d60f3957c11e46500ae",
              "display_name": "Lumpo"
            },
            "comment_text": "kaka2"
          },
          {
            "comment_posted": "2018-05-24T09:47:42.752Z",
            "_id": "5b068a3e2fdd6f141d5ba995",
            "comment_from": {
              "photo_url": "png",
              "_id": "5af16d60f3957c11e46500ae",
              "display_name": "Lumpo"
            },
            "comment_text": "kaka!"
          }
        ],
        "_id": "5b0689f03fb2fd1404f1854d",
        "post_description": "get out\r\n",
        "post_by": {
          "photo_url": "png",
          "_id": "5af16d60f3957c11e46500ae",
          "display_name": "Lumpo"
        },
        "__v": 2
      }
    ]

Spread operator is not working logs the same thing with the Object.assign method // console.log(mutator)

[
  {
    "post_img": [],
    "_id": "5b0694cc7925c914e4d95dda",
    "post_description": "test",
    "post_by": {
      "_id": "5af16d60f3957c11e46500ae",
      "display_name": "Lumpo",
      "photo_url": "png"
    },
    "post_comments": [
      {
        "_id": "5b0694d67925c914e4d95ddb",
        "comment_from": {
          "photo_url": "png",
          "_id": "5af16d60f3957c11e46500ae",
          "display_name": "Lumpo"
        },
        "comment_text": "This comment should only be in the mutator ",
        "comment_posted": "2018-05-24T10:32:54.937Z"
      }
    ],
    "post_date": "2018-05-24T10:32:44.613Z",
    "__v": 0
  }
]

// console.log(this.state.getStatus);

[
  {
    "post_img": [],
    "_id": "5b0694cc7925c914e4d95dda",
    "post_description": "test",
    "post_by": {
      "_id": "5af16d60f3957c11e46500ae",
      "display_name": "Lumpo",
      "photo_url": "png"
    },
    "post_comments": [
      {
        "_id": "5b0694d67925c914e4d95ddb",
        "comment_from": {
          "photo_url": "png",
          "_id": "5af16d60f3957c11e46500ae",
          "display_name": "Lumpo"
        },
        "comment_text": "This comment should only be in the mutator ",
        "comment_posted": "2018-05-24T10:32:54.937Z"
      }
    ],
    "post_date": "2018-05-24T10:32:44.613Z",
    "__v": 0
  }
]

5 Answers 5

2
const mutator =  Object.assign([], this.state.getStatus);

its doing shallow/reference copy of array.

So,original array is copied as it is using reference.

Use spread operator to create new copy of array and then do JSON.stringify followed by JSON.parse.U need a deep copy.

let mutator = [...this.state.getStatus];
mutator = JSON.parse(JSON.stringify(mutator));
Sign up to request clarification or add additional context in comments.

9 Comments

It's doing the same thing the state still gets modified.
Could u please share the this.state.getStatus output
@markuspaterson please check the updates answer last line
Check my question i put the output there
Parsing the mutator works but is this ideal? Why do i need to do it ?
|
1

you can copy your array something like this :

 const mutator =  [...this.state.getStatus];

1 Comment

Since the array has objects this is will not solve the mutation problem. You need to someArray.map(item=>(item._id===data._id)?{...item,newProp:data.newProp}:item)
1
Object.assign([], this.state.getStatus)

[] is an array, not an object. This is likely causing a problem.

Edit: See Josh’s comment, it is an object, but also an array. But the behaviour will be different to if it were an object object.

2 Comments

It's an array and an object.
Since the array has objects this is will not solve the mutation problem. You need to someArray.map(item=>(item._id===data._id)?{...item,newProp:data.newProp}:item)
0

The quickest way to make a copy of an existing array without copying a reference is the following:

const mutator = this.state.getStatus.slice(0);

as described here https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Array/slice

1 Comment

Since the array has objects this is will not solve the mutation problem. You need to someArray.map(item=>(item._id===data._id)?{...item,newProp:data.newProp}:item)
0

Your state is an object containing an array of objects.

First you copy state and reset the array getStatus with getStatus mapped. When the status item is found that needs to change you copy that item but set post_comments with another value (see code below).

this.setState({
  ...this.state,//copy state
  getStatus: this.state.getStatus.map(//getStatus is a new array
    (item,index)=>
      (item._id===data._id)//if item._id is data._id
        ? {...item,post_comments:data.post_comments}//copy item but reset post_comments
        : item//not the item we are looking for, return item (not changed copy)
  )
})

If you need more help the please let me know.

Using JSON.parse(JSON.stringify(state))) will cause all components to re render even if their part of the state did not change (deep copy versus shallow copy). You can use shouldComponentUpdate to see if the state actually changed and tell react not to re render components where this did not happen. However; since you are deep copying everything (not only the items that changed) you cannot do this.

Here is an example of a base component that checks if the state passed to it actually changed reference and should re render:

import React from 'react';
class OnlyIfChanged extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    return nextProps.state !== this.props.state;
  }
}
export default OnlyIfChanged;

4 Comments

Works amazingly too but i don't understand it haha
@markuspaterson the spread syntax({...this.state}) makes a shallow copy of the state and the ,getStatus:this.state.getStatus.map re assigns the getStatus property of the shallow copied state. The this.state.getStatus.map maps the getStatus array to a new array, if the item with the correct id is found then item is copied using spread {...item} and post_comments is re assigned in that copy.
@markuspaterson Using JSON.parse(JSON.stringify(someObject)) is a far more expensive operation and has the disadvantage that every member of the state has a new reference (also the items that did not change). This would cause components to re render that don't need to re render.
@markuspaterson Updated my answer with the disadvantages using JSON.parse(JSON.stringify

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.