lharby
lharby

Reputation: 3265

React validate input with regex and useState hook

I'm trying to build a Password validator. My issue is that the boolean valid is always false even when the regex is tested to be correct. I am actually using typescript in my app, but I've made a demo here just in JSX:

https://codesandbox.io

The App.js file looks like this:

import React, { useState } from "react";
import "./styles.css";

const App = () => {
  const [inputValue, setInputValue] = useState('');
  const [valid, setValid] = useState(false);
  const isValidPasswordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[#$@!%&*?])[A-Za-z\d#$@!%&*?]{12,}$/;
  //const isValidPasswordRegex = /^\d+$/;

  const validatePassword = (value) => {
    if (!isValidPasswordRegex.test(value)) {
      setValid(!valid);
    } else {
      setValid(valid);
    }
    return valid;
  };

  function onChange(e) {
    setInputValue(e.target.value);
    setValid(validatePassword(e.target.value));
  }
  
  return(
    <div>
      <div>Fill the password input and click elsewhere to blur the field</div>
      <input
        className={`${valid ? 'success' : 'error'}`}
        onChange={onChange}
        onBlur={onBlur}
        value={inputValue}
      />
      <div>{valid.toString()}</div>
    </div>
  );
}

export default App;

I've looked ay many similar questions on SO that seem to be using a very similar logic, but I assume that the app is re-rendering and valid is being set to false on each render. I thought useState was the correct implementation and binding this to an onChange event would test the input value each time it is changed. I was concerned my regex was not correct so I also tested a simpler condition (which is commented out here) but that is not the issue.

Upvotes: 1

Views: 4229

Answers (1)

Bennett Dams
Bennett Dams

Reputation: 7033

There are two problems with your code.

1.) validatePassword uses the useState's setter, but as a value to set it is using the valid state (e.g. setValid(!valid)). As useState's setter works asynchronously, this will not use the "latest" state. Just use true or false directly, no reason to use valid. That way you're also safe if valid is changed somewhere else in the code.

2.) In your onChange, you're calling validatePassword, which therefore is always setting the value of valid to false (or void, your example at StackOverflow differs from your example of the CodeSandbox), because you're calling validatePassword in your setValid.

Just call validatePassword(...) instead of setValid(validatePassword(...)), as validatePassword is already calling setValid:

  function onChange(e) {
    setInputValue(e.target.value);
    setValid(validatePassword(e.target.value));
  }

should be

  function onChange(e) {
    setInputValue(e.target.value);
    validatePassword(e.target.value);
  }

Another option would be to return true or false from validatePassword and don't call setValid inside there, but outside with the return value of validatePassword.


Working CodeSandbox: https://codesandbox.io/s/patient-worker-7fs84

I don't understand your regex, so I replaced it with a simple "every character is a letter" for demonstration purposes.

Upvotes: 1

Related Questions