Matt X
Matt X

Reputation: 244

Form Validation Logic Problem ( Using React useState Hook)

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

Answers (1)

McKay M
McKay M

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

Related Questions