0

Okay so I have 1 states in React.js, with 2 key,val pairs. Length validation & character validation as shown below:

const [validation, setValidationState] = useState({
   lengthValidation: "",
   characterValidation: "",
});

These 2 states are wrapped in a function named promptValidation ( I'll show all code at the end )

Within this function I have 2 conditionals, in which only 1 works at a time. Those conditionals are shown below:

// if password length doesn't meet criteria set state to error message
if (
  props.promptstate["length"] < 8 ||
  props.promptstate["length"] > 128 ||
  Number.isNaN(props.promptstate["length"])
) {
  setValidationState({
    ...validation,
    ["lengthValidation"]:
      "Password length must be between 8-128 characters...",
  });
} else {
  setValidationState({
    ...validation,
    ["lengthValidation"]: "",
  });
}

// if password characters aren't selected at all set state to error message
if (
  !props.promptstate["specialCharacters"] &&
  !props.promptstate["upperCase"] &&
  !props.promptstate["lowerCase"] &&
  !props.promptstate["numbers"]
) {
  setValidationState({
    ...validation,
    ["characterValidation"]:
      "At least one character type should be selected...",
  });
}
// if password does meet require set state back to empty string
else {
  setValidationState({
    ...validation,
    ["characterValidation"]: "",
  });
}

Now my issue is upon invoking my function on a button click only one of the conditionals run. And it's pretty weird, considering it's the second conditional that runs.

The weirder part is, if I comment out one conditional and my function meets that conditional it will run, if I switch the commenting of the other conditional and my function meets criteria then that will run as well.

The issue present it's self when both conditionals are in play.

My expected behavior is for both conditionals to update the state upon both of them meeting the conditionals criteria.

Below is the the state and full function that's being ran on click.

const [validation, setValidationState] = useState({
   lengthValidation: "",
   characterValidation: "",
});

const promptValidation = () => {
// if password length doesn't meet criteria set state to error message
if (
  props.promptstate["length"] < 8 ||
  props.promptstate["length"] > 128 ||
  Number.isNaN(props.promptstate["length"])
) {
  setValidationState({
    ...validation,
    ["lengthValidation"]:
      "Password length must be between 8-128 characters...",
  });
} else {
  setValidationState({
    ...validation,
    ["lengthValidation"]: "",
  });
}

// if password characters aren't selected at all set state to error message
if (
  !props.promptstate["specialCharacters"] &&
  !props.promptstate["upperCase"] &&
  !props.promptstate["lowerCase"] &&
  !props.promptstate["numbers"]
) {
  setValidationState({
    ...validation,
    ["characterValidation"]:
      "At least one character type should be selected...",
  });
}
// if password does meet require set state back to empty string
else {
  setValidationState({
    ...validation,
    ["characterValidation"]: "",
  });
}

console.log(validation);

};

Please assist if possible, thank you. I'm a noob when it comes to react & es6.

Andrew Venson
  • 203
  • 5
  • 14
  • Can you please reproduce this as a code sandbox. The act of doing that will likely also help you debug it. – dwjohnston Apr 30 '20 at 06:35
  • 1
    Does this answer your question? [When to use functional setState](https://stackoverflow.com/questions/48209452/when-to-use-functional-setstate) – Emile Bergeron Apr 30 '20 at 06:41
  • 1
    @EmileBergeron yes I'm pretty sure this does answer the question as well! Thank you for sharing that post. – Andrew Venson Apr 30 '20 at 07:08

1 Answers1

2

The issue that you see happens because state updates within event handlers are batched in react and so your state gets overridden if both the conditions are met when you trigger 2 state update calls.

The solution here is to use the callback approach to update state

// if password length doesn't meet criteria set state to error message
if (
  props.promptstate["length"] < 8 ||
  props.promptstate["length"] > 128 ||
  Number.isNaN(props.promptstate["length"])
) {
  setValidationState(prevValidation => ({
    ...prevValidation,
    ["lengthValidation"]:
      "Password length must be between 8-128 characters...",
  }));
} else {
  setValidationState(prevValidation => ({
    ...prevValidation,
    ["lengthValidation"]: "",
  }));
}

// if password characters aren't selected at all set state to error message
if (
  !props.promptstate["specialCharacters"] &&
  !props.promptstate["upperCase"] &&
  !props.promptstate["lowerCase"] &&
  !props.promptstate["numbers"]
) {
  setValidationState( prevValidation => ({
    ...prevValidation,
    ["characterValidation"]:
      "At least one character type should be selected...",
  }));
}
// if password does meet require set state back to empty string
else {
  setValidationState(prevValidation => ({
    ...validation,
    ["characterValidation"]: "",
  }));
}
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • Interesting. It looks like this will fix my issue. But considering I'm new to this, I'm not too entirely sure why a callback function would fix this approach rather than my approach. I'm not understanding the logic I guess. – Andrew Venson Apr 30 '20 at 06:58
  • 1
    As I said, state updates are batched in react, if you use callback, it gurantees to provide the previous value. Kind of like removing the batching – Shubham Khatri Apr 30 '20 at 07:00
  • Ahhh I see, I'm going to implement the logic tomorrow, and mark your answer as the answer, just waiting on a little feedback from my mentor. Thank you man! – Andrew Venson Apr 30 '20 at 07:06
  • Question: do you recommend changing the state every time with call back functions, to be safe? Or does it only matter in these particular instances. – Andrew Venson Apr 30 '20 at 16:13
  • 2
    I think this post will give you details about it: https://stackoverflow.com/questions/48209452/when-to-use-functional-setstate/48209870#48209870 In short only use callback when you update current state based on previous one – Shubham Khatri Apr 30 '20 at 17:30