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=".8.6/umd/react.production.min.js"></script>
<script src=".8.6/umd/react-dom.production.min.js"></script>
<div id='root'></div>
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./ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id='root'></div>
By clicking the square we trigger 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...
Share Improve this question edited Feb 7, 2020 at 5:26 Michael asked Feb 7, 2020 at 5:20 MichaelMichael 9588 silver badges19 bronze badges 9- thats a good question, trying to find out what is happening. – Tinu Jos K Commented Feb 7, 2020 at 5:40
- @gaditzkhori it's a shallow copy, thats why both prevState and newState is modifying the same x value, and on each renders the prevState actually gets the updated previous value, then why can't we set it onto the state and why is it not displayed in the div? – Tinu Jos K Commented Feb 7, 2020 at 5:55
-
1
setState
is paring the previous state object to the object you're returning. If both these reference the same object, no render takes place. Despite the fact that prevState has been updated, it's still the same object. – FuriousD Commented Feb 7, 2020 at 5:58 -
2
newState is a shallow copy but you changed
x
which is the same reference.React doesn't know that it is changed deeply and therefore doesn't rerender.so basicly it's pure js not some React magic.thats the whole idea of shallow rendering. – gadi tzkhori Commented Feb 7, 2020 at 6:02 - 1 you are right! prevState gets updated as it should be, but no rerender takes place because of the deep change of the object. forcing a rerender by changing some extra dummy state variable shows that the count goes up correctly – Michael Commented Feb 7, 2020 at 6:53
1 Answer
Reset to default 5Consider 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]