Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copying React State object; modifying copied object changes state object

I am trying to copy a state object:

@boundMethod
private _onClickDeleteAttachment(attachmentName: string): void {
    console.log("_onClickDeleteAttachment | this.state.requestApproval[strings.Attachments]: ", this.state.requestApproval[strings.Attachments]);

    let requestApprovalClone = {... this.state.requestApproval}

    if (requestApprovalClone === this.state.requestApproval) {
        console.log("they are ===");
    }
    else {
        console.log(" they are not ===");
    }

    _.remove(requestApprovalClone[strings.Attachments], (attachment: any) => {
        return attachment.FileName === attachmentName;
    })

    console.log("_onClickDeleteAttachment | this.state.requestApproval[strings.Attachments]: ", this.state.requestApproval[strings.Attachments]);
    console.log("_onClickDeleteAttachment | requestApprovalClone[strings.Attachments]: ", requestApprovalClone[strings.Attachments]);
}

enter image description here

The state object is being altered too. From what I have read, I shouldn't mutate a state object but only change it with setState.

How can I correct this?

like image 482
Holden1515 Avatar asked Apr 02 '19 20:04

Holden1515


People also ask

How do you avoid mutating state in React?

In React, the state is immutable. In simple terms it means that you should not modify it directly. Instead a new object should be created to set the state using setState .

What happens when you modify the state of a React component?

Whenever the data in the state store changes, react will trigger an re-render with the new state which the view consumes and shows it on the screen.

Why is state immutable in React?

Why Immer works well for React State Immutability? In React, using an Immutable state enables quick and cheap comparison of the state tree before and after a change. As a result, each component decides whether to re-rendered or not before performing any costly DOM operations.


2 Answers

You are getting that behavior, because the

let requestApprovalClone = {... this.state.requestApproval}

is only shallow copying the data, your attachments property has some nested objects and it keeps the same reference and therefore when changing it, the cloned object gets altered and the state too.

To avoid that, you can perform another copy of your attachments property like this :

let attachments = [...requestApprovalClone[strings.Attachments]];
_.remove(attachments, function (attachment)  {
  return attachment.FileName === attachmentName;
});

Changing the attachments variable content won't afftect the state anymore.

you can read more about that behavior here

like image 118
Abdelkarim EL AMEL Avatar answered Oct 02 '22 12:10

Abdelkarim EL AMEL


It has to to with the way that JS handles their const references.

For those who feel adventerous:

let requestApprovalClone = JSON.parse(JSON.stringify(this.state.requestApproval));

// modify requestApprovalClone ...

this.setState({
  requestApproval:requestApprovalClone
})

It would be interesting if Object.assign({},this.state.requestApproval); is faster than the whole JSON stringify/parse stuff or vice versa

like image 21
G43beli Avatar answered Oct 02 '22 11:10

G43beli