Reputation: 244
I am trying to do some form validation. I have two controlled elements in my form: a messages textbox and a recipients textbox. So, I have state.message
(a string) and state.recipients
(an array) which control some textboxes (when you type in the textboxes, these values of state
change). The state is working properly. That is, when I type in the messages textbox, state.messages
gets updated. When I type in the recipients box, state.recipients
gets updated. invalid
is defined with the useState
hook like so:
const [invalid, setInvalid] = useState({
message: false,
recipients: false,
})
I want invalid.messages
to be true
when state.messages
is empty, and invalid.recipients
to be true when state.recipients
is empty. Here is my validation code, which I am having some trouble with:
if (state.recipients.length === 0) {
setInvalid({
...invalid,
recipients: true,
});
} else {
setInvalid({
...invalid,
recipients: false,
});
}
if (state.message === '') {
setInvalid({
...invalid,
message: true,
});
} else {
setInvalid({
...invalid,
message: false,
});
}
My conditions are working, though. state.recipients.length
is indeed equal to 0
when the recipients textbox is empty, and state.message
is indeed ''
when the message textbox is blank. Now, the interesting part is that when both elements of the form are empty (that is, recipients and message are both empty), invalid
is equal to
{
recipients: false,
message: true
}
This is extremely weird because I don't see any flaw in the logic I am using to change the state. The weirder part is that when I flip the if statements in this validation logic (that is, I check the message state first and then the recipients state) then the reverse happens: state looks like:
{
recipients: true,
message: false
}
I don't know why this is happening, so can you point out the flaw in my logic?
Edit: Some event handler code for the textboxes:
For the messages textbox: onChange={(e) => { setState({ ...state, message: e.target.value }); }}
For the recipients textbox (which intentionally overrides the first element in state.recipients
):
onChange={(e, val) => setState({
...state,
recipients: val,
})}
But this code really isn't the issue, because the state is being handled properly and it is empty when it supposed to be.
Upvotes: 1
Views: 419
Reputation: 448
State updates are asynchronous, so when call setInvalid
and add the previous state using ...invalid
, the same old state is being used for both updates.
In other words, when the code you pasted is called, the first setInvalid
to execute doesn't finish updating the state before the second one starts executing and pulls in the state using ...invalid
.
As a result, only messaged
gets set properly.
You can likely resolve this issue by making both variables separate state objects.
const [invalidRecipiants, setInvalidRecipiants] = useState(true);
const [invalidMessages, setInvalidMessages] = useState(true);
Upvotes: 2