I noticed a behavior I cant understand how to solve, I never had it while I was writing react class based using this.setState but I get it while using useState hook.
for example:
const Main = () => {
const [values, setValues] = useState({val1: '', val2: '', val3: ''})
const func1 = async () => {
await setValues({
...values,
val1: '111111111'
});
await func2();
}
const func2 = async () => {
result = (fetch some data);
await setValues({
...values,
val2: result
});
}
};
now if you run func1, val1 will be changed but as soon as func2 finishes and we setValues the second time val1 will get overwritten and val2 will contain value.
what am I doing wrong and how can I fix this ?
Thanks in Advance!
Edit:
when using Hooks I cant see what is the acctual anme of the value entered in the React Chrome Dev tool. is there a way to fix this ? when I was having one useState containing an object I could see the titles of each object key... now its hidden -_-
I noticed a behavior I cant understand how to solve, I never had it while I was writing react class based using this.setState but I get it while using useState hook.
for example:
const Main = () => {
const [values, setValues] = useState({val1: '', val2: '', val3: ''})
const func1 = async () => {
await setValues({
...values,
val1: '111111111'
});
await func2();
}
const func2 = async () => {
result = (fetch some data);
await setValues({
...values,
val2: result
});
}
};
now if you run func1, val1 will be changed but as soon as func2 finishes and we setValues the second time val1 will get overwritten and val2 will contain value.
what am I doing wrong and how can I fix this ?
Thanks in Advance!
Edit:
when using Hooks I cant see what is the acctual anme of the value entered in the React Chrome Dev tool. is there a way to fix this ? when I was having one useState containing an object I could see the titles of each object key... now its hidden -_-
Share Improve this question edited Oct 2, 2019 at 14:11 user3657538 asked Oct 2, 2019 at 13:14 user3657538user3657538 2151 gold badge4 silver badges10 bronze badges 2-
React Hooks works really well when you break your app into small sections,. This is avoids plex problems with closures etc, that your getting here with
values
. Eg. In your example maybe have another ponent called<Results/>
etc. Separation of concerns etc gets better too. Also GUI updates are more efficient too, – Keith Commented Oct 2, 2019 at 13:28 - Possible duplicate of useState set method not reflecting change immediately – mbojko Commented Oct 2, 2019 at 13:30
4 Answers
Reset to default 3You're spreading the state before updating the correspondent property, try to chunk
your state
const Main = () => {
const [value1, setValue1] = useState(null)
const [value2, setValue2] = useState(null)
const func1 = async () => {
setValue1('foo')
await func2();
}
const func2 = async () => {
result = (fetch some data);
await setValue2('foo')
}
};
Here is what is happening
setValues
is called changingval1
(state isn't updated yet)setValues
is called again changingval2
andspreading
the rest
By the time setValues
spreads values
val1
still holds it's initial value, overwriting the first setValues
call. Remember, changes in state are reflected asynchronously
React useState
also takes a functional updater to update your ponent state similarly to how class-based ponent's setState
works.
Note
Unlike the setState method found in class ponents, useState does not automatically merge update objects. You can replicate this behavior by bining the function updater form with object spread syntax:
setState(prevState => {
// Object.assign would also work
return {...prevState, ...updatedValues};
});
So in your code you could update as follows:
const Main = () => {
const [values, setValues] = useState({val1: '', val2: '', val3: ''});
const func1 = async () => {
await setValues(prevState => ({
...prevState,
val1: '111111111'
}));
await func2();
}
const func2 = async () => {
result = (fetch some data);
await setValues(prevState => ({
...prevState,
val2: result
}));
}
};
I haven't tested it yet but maybe it could work as an alternative.
Sometimes, when we need to get the value in the state, we usually do the spread operator within the state but it doesn't guarantees that the value is the correct one. For those cases, we better call setState
with a callback which takes the previous value from state. So, you can use something like this:
setValues(prev => ({...prev, val1:'11111'}));
The behaviour of the code bees clear once we note that values
variable in func2 is clousre on values
at outer scope, which is really a copy of state taken at the time useState
was called.
So what your spreading in func2
is is stale copy of your state.
One way of correcting this would be to use functional update
setValues((values) => ({...values, val2: result}));
This will make sure that you are using updated value of the state.