Returning the (changed) previous state within a setState
that one gets from the useState
hook doesn't seem to alter state. Run the following straight forward snippet
function App(){
const [state, setState] = React.useState([{x: 0}])
function changeCount(){
setState(prevState => {
console.log('before', prevState[0])
const newState = [...prevState]
newState[0].x += 1 //the shallow copy newState could potentially change state
console.log('after', prevState[0])//here x gets bigger as expected
return prevState //instead of newState we return the changed prevState
})
}
//checking state shows that x remained 0
return <div className='square' onClick={changeCount}>{state[0].x}</div>
}
ReactDOM.render(<App/>, document.getElementById('root'))
.square{
width: 100px;
height: 100px;
background: orange;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id='root'></div>
setState
. Within setState
we make a shallow copy newState
of the previous state. By changing the copy we change the prevState
(maybe unintentionally) as the console confirms. Returning the changed previous state form setState
however doesn't change the state as the count remains 0. If we were to return the newState
, the behavior would be as expected.
Repeating this, shows that prevState
gets bigger, it just doesn't seem to represent the previous state anymore.
Why is that? I made this minimal example on codepen...
Consider that object assignment is just a reference assignment, never a copy
obj1 = {x:42, y:99};
obj2 = obj1; // obj1 and obj2 both reference the same object
obj1.x += 1;
obj2.y += 1;
console.log(obj1.x, obj1.y);
console.log(obj2.x, obj2.y); // prints the same thing since obj1 and obj2 are the same object
In the above example, obj1
is initialized to point to a new object with properties x and y. When obj2=obj1
is made, this is not a copy of obj1 into obj2, but rather obj1 and obj2 now reference the same object.
Hence, when the console.log statements print, they print the same thing because they are both printing property values from the same object.
Similarly, the additional reference to the original object is being made when the shallow copy from prevState to newState occurrs.
obj = {x:42, y:99};
prevState[0] = obj; // prevState[0] is a reference to obj. prevState[0] and obj point to the same exact thing
newState = [...prevState]; // shallow copy, but newState[0] is an object reference. newState[0] and prevState[0] both point to "obj"
newState[0].x += 1; // is actually updating the original object assigned to prevState[0]