facebook / react

The library for web and native user interfaces.
https://react.dev
MIT License
227.48k stars 46.41k forks source link

Changing local copy of a state in a method automatically changes the state as well. #10220

Closed abhishekjain2604 closed 7 years ago

abhishekjain2604 commented 7 years ago

I want to report a bug(or at least that's what I think it is)

I assigned the value of a state(say X) to a local variable in a method. Making any change in that variable and calling setState() to change the value of any other state(say Y) changes the value of X as well. Exactly the same change that I did in the local variable. I am not using setState to change the value of X. I am using it to change the value of Y. Yet X changes as well in exactly the same way as I changed the local variable which was earlier initialised using X.

Here is the demo of the problem: https://jsfiddle.net/jainabhishek2604/vkms2acq/2/

Clicking on the button changes the numbers which are rendered using the state "test". Clicking the button invokes a method where the value of X is stored in a variable "arr" whose first indexed is then spliced. setState is them called passing an object changing the value of the state "temp". But the "test" is updated as well with the value of "arr".

I am using react version 15.6.1 on chromium running on Ubuntu 16.04 LTS.

aweary commented 7 years ago

Hey @abhishekjain2604! As far as I can tell, what you're seeing is expected behavior. With objects or arrays, when you create a local variable you're not creating a copy of the value, you're just creating another reference to the original value.

So when you do:

let arr = this.state.test;

arr and this.state.test are the same array. It doesn't matter if you mutate the array using the arr reference or not, you're still mutating this.state.test. So when you do:

arr.splice(0,1);

It's the exact same as doing:

this.state.test.splice(0, 1);

Since they reference the same value. In this case, you're removing the first element from the array using splice. Then when you call setState you trigger a new render. Now it will render with the new value of this.state.test. Even though you didn't call setState with test, you did update its value via mutation. This is typically why we recommend not mutating this.state, as it can lead to confusing behavior.

You can use a non-mutative method like slice to create a local copy instead, and then use setState to update the value. For example

var arr = this.state.test.slice(0);
// arr is a new array, separate from this.state.test
arr.splice(0, 1);
this.setState({ test: arr })

Hope that helps!

abhishekjain2604 commented 7 years ago

Thank you so much for that.

I didn't know Javascript handles array and object assignments through references. And all the time I was thinking that React was having issues.

Thank you so much for clearing this and the workaround that you suggested.