最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Formik, Yup Password Strength Validation with React - Stack Overflow

programmeradmin20浏览0评论

I am fairly new to React, and i have a sign up page where i have a password field to be validated with a Regex.

I am using Formik and Yup for validations, but i have encountered an error where it says the property where the length function is being called is undefined when i type in the "password" field.

There is a function called "matches" in yup, which i am trying to utilize for checking a regex. That is when i get this error. I removed this validation, after which other validations set to password field worked fine.

Below is the code for the SignUp.js file :-

import React from "react";
import Button from "react-bootstrap/Button";
import Form from "react-bootstrap/Form";
import "./SignUp.css";
import * as yup from "yup";
import { Formik } from "formik";

const schema = yup.object({
  username: yup.string().required('Please Enter a username'),
  email: yup
    .string()
    .email()
    .required('Please Enter your Email'),
  confirmEmail: yup
    .string()
    .email()
    .required()
    .oneOf([yup.ref("email"), null], "Emails must match"),
  password: yup
    .string()
    .required('Please Enter your password')
    .matches(
      "^(?=.*[A-Za-z])(?=.*d)(?=.*[@$!%*#?&])[A-Za-zd@$!%*#?&]{8,}$",
      "Must Contain 8 Characters, One Uppercase, One Lowercase, One Number and one special case Character"
    ),
  confirmPassword: yup
    .string()
    .required()
    .oneOf([yup.ref("password"), null], "Passwords must match")
});

const SignUp = props => {
  return (
    <Formik
    validationSchema={schema}
    onSubmit={console.log}
    initialValues={{
      username: "",
      email : "",
      confirmEmail : "",
      password: "",
      confirmPassword : ""
    }}
  >
  {({
    handleSubmit,
    handleChange,
    handleBlur,
    values,
    touched,
    isValid,
    errors,
  }) => (
    <div className="SignUpForm">
      <h1 className="SignInHeading">SIGN UP</h1>
      <Form noValidate onSubmit={handleSubmit}>
        <Form.Group controlId="formBasicUserName">
          <Form.Control
            size="lg"
            className="SignUpFormControls"
            type="text"
            name="username"
            value={values.username}
            onChange={handleChange}
            placeholder="Username"
            isInvalid={!!errors.username}
          />
          <Form.Control.Feedback className="FeedBack" type="invalid">
          {errors.username}
        </Form.Control.Feedback>
        </Form.Group>

        <Form.Group controlId="formBasicEmail">
          <Form.Control
            type="email"
            placeholder="Email"
            value={values.email}
            onChange={handleChange}
            name="email"
            className="SignUpFormControls"
            size="lg"
            isInvalid={!!errors.email}
          />
          <Form.Control.Feedback className="FeedBack" type="invalid">
          {errors.email}
        </Form.Control.Feedback>
        </Form.Group>

        <Form.Group controlId="formBasicConfirmEmail">
          <Form.Control
            type="email"
            className="SignUpFormControls"
            size="lg"
            name="confirmEmail"
            value = {values.confirmEmail}
            onChange={handleChange}
            placeholder="Confirm Email"
            isInvalid={!!errors.confirmEmail}
          />
          <Form.Control.Feedback className="FeedBack" type="invalid">
          {errors.confirmEmail}
        </Form.Control.Feedback>
        </Form.Group>

        <Form.Group controlId="formBasicPassword">
          <Form.Control
            className="SignUpFormControls"
            size="lg"
            type="password"
            name="password"
            value={values.password}
            onChange={handleChange}
            placeholder="Password"
            isInvalid={!!errors.password}
          />
          <Form.Control.Feedback className="FeedBack" type="invalid">
          {errors.password}
        </Form.Control.Feedback>
        </Form.Group>

        <Form.Group controlId="formBasicConfirmPassword">
          <Form.Control
            className="SignUpFormControls"
            size="lg"
            name="confirmPassword"
            onChange={handleChange}
            type="password"
            value={values.confirmPassword}
            placeholder="Confirm Password"
            isInvalid={!!errors.confirmPassword}
          /><Form.Control.Feedback className="FeedBack" type="invalid">
          {errors.confirmPassword}
        </Form.Control.Feedback>
        </Form.Group>

        <Button variant="primary" className="SignUpButton" type="submit">
          Sign Up
        </Button>
        <Form.Text>
          Already a User?{" "}
          <a href="#signin" onClick={props.toggle}>
            Sign In
          </a>
        </Form.Text>
      </Form>
    </div>)}
    </Formik>
  );
};

export default SignUp;

Here is the error in chrome console :-

formik.esm.js:721 Uncaught (in promise) TypeError: Cannot read property 'length' of undefined
    at yupToFormErrors (formik.esm.js:721)
    at formik.esm.js:276

I am fairly new to React, and i have a sign up page where i have a password field to be validated with a Regex.

I am using Formik and Yup for validations, but i have encountered an error where it says the property where the length function is being called is undefined when i type in the "password" field.

There is a function called "matches" in yup, which i am trying to utilize for checking a regex. That is when i get this error. I removed this validation, after which other validations set to password field worked fine.

Below is the code for the SignUp.js file :-

import React from "react";
import Button from "react-bootstrap/Button";
import Form from "react-bootstrap/Form";
import "./SignUp.css";
import * as yup from "yup";
import { Formik } from "formik";

const schema = yup.object({
  username: yup.string().required('Please Enter a username'),
  email: yup
    .string()
    .email()
    .required('Please Enter your Email'),
  confirmEmail: yup
    .string()
    .email()
    .required()
    .oneOf([yup.ref("email"), null], "Emails must match"),
  password: yup
    .string()
    .required('Please Enter your password')
    .matches(
      "^(?=.*[A-Za-z])(?=.*d)(?=.*[@$!%*#?&])[A-Za-zd@$!%*#?&]{8,}$",
      "Must Contain 8 Characters, One Uppercase, One Lowercase, One Number and one special case Character"
    ),
  confirmPassword: yup
    .string()
    .required()
    .oneOf([yup.ref("password"), null], "Passwords must match")
});

const SignUp = props => {
  return (
    <Formik
    validationSchema={schema}
    onSubmit={console.log}
    initialValues={{
      username: "",
      email : "",
      confirmEmail : "",
      password: "",
      confirmPassword : ""
    }}
  >
  {({
    handleSubmit,
    handleChange,
    handleBlur,
    values,
    touched,
    isValid,
    errors,
  }) => (
    <div className="SignUpForm">
      <h1 className="SignInHeading">SIGN UP</h1>
      <Form noValidate onSubmit={handleSubmit}>
        <Form.Group controlId="formBasicUserName">
          <Form.Control
            size="lg"
            className="SignUpFormControls"
            type="text"
            name="username"
            value={values.username}
            onChange={handleChange}
            placeholder="Username"
            isInvalid={!!errors.username}
          />
          <Form.Control.Feedback className="FeedBack" type="invalid">
          {errors.username}
        </Form.Control.Feedback>
        </Form.Group>

        <Form.Group controlId="formBasicEmail">
          <Form.Control
            type="email"
            placeholder="Email"
            value={values.email}
            onChange={handleChange}
            name="email"
            className="SignUpFormControls"
            size="lg"
            isInvalid={!!errors.email}
          />
          <Form.Control.Feedback className="FeedBack" type="invalid">
          {errors.email}
        </Form.Control.Feedback>
        </Form.Group>

        <Form.Group controlId="formBasicConfirmEmail">
          <Form.Control
            type="email"
            className="SignUpFormControls"
            size="lg"
            name="confirmEmail"
            value = {values.confirmEmail}
            onChange={handleChange}
            placeholder="Confirm Email"
            isInvalid={!!errors.confirmEmail}
          />
          <Form.Control.Feedback className="FeedBack" type="invalid">
          {errors.confirmEmail}
        </Form.Control.Feedback>
        </Form.Group>

        <Form.Group controlId="formBasicPassword">
          <Form.Control
            className="SignUpFormControls"
            size="lg"
            type="password"
            name="password"
            value={values.password}
            onChange={handleChange}
            placeholder="Password"
            isInvalid={!!errors.password}
          />
          <Form.Control.Feedback className="FeedBack" type="invalid">
          {errors.password}
        </Form.Control.Feedback>
        </Form.Group>

        <Form.Group controlId="formBasicConfirmPassword">
          <Form.Control
            className="SignUpFormControls"
            size="lg"
            name="confirmPassword"
            onChange={handleChange}
            type="password"
            value={values.confirmPassword}
            placeholder="Confirm Password"
            isInvalid={!!errors.confirmPassword}
          /><Form.Control.Feedback className="FeedBack" type="invalid">
          {errors.confirmPassword}
        </Form.Control.Feedback>
        </Form.Group>

        <Button variant="primary" className="SignUpButton" type="submit">
          Sign Up
        </Button>
        <Form.Text>
          Already a User?{" "}
          <a href="#signin" onClick={props.toggle}>
            Sign In
          </a>
        </Form.Text>
      </Form>
    </div>)}
    </Formik>
  );
};

export default SignUp;

Here is the error in chrome console :-

formik.esm.js:721 Uncaught (in promise) TypeError: Cannot read property 'length' of undefined
    at yupToFormErrors (formik.esm.js:721)
    at formik.esm.js:276
Share Improve this question edited Apr 1, 2019 at 11:07 Shashaank V V asked Apr 1, 2019 at 8:54 Shashaank V VShashaank V V 7401 gold badge7 silver badges20 bronze badges 4
  • The error does not say that length is undefined, it says that the property length doesn't exist on undefined. – phuzi Commented Apr 1, 2019 at 9:01
  • Rather than add images of error messages, please copy paste the text in to the question. – phuzi Commented Apr 1, 2019 at 9:02
  • I have made the change. Do you have any idea why this is popping up? – Shashaank V V Commented Apr 1, 2019 at 10:07
  • It would be a good practice and much easier to manage simpler to validate each password criteria more granularly. eg match on too short of a length, then on no number, etc. – Kzqai Commented Dec 25, 2020 at 0:13
Add a comment  | 

13 Answers 13

Reset to default 78

You need to pass an actual RegExp object to matches, not a string. Just replace the double quotes with forward slashes in your password schema:

EDIT: Updated to use regex from @Bren

password: yup
    .string()
    .required('Please Enter your password')
    .matches(
      /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#\$%\^&\*])(?=.{8,})/,
      "Must Contain 8 Characters, One Uppercase, One Lowercase, One Number and One Special Case Character"
    ),

This is what I finally got to work.

 password: Yup.string()
          .required('Please Enter your password')
          .matches(

            /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#\$%\^&\*])(?=.{8,})/,
            "Must Contain 8 Characters, One Uppercase, One Lowercase, One Number and One Special Case Character"
          ),

I took this regex string from this article: https://www.thepolyglotdeveloper.com/2015/05/use-regex-to-test-password-strength-in-javascript/

Okay, after a couple of hours of tinkering around, i decided to do my own custom validation instead.

Here is what i did :-

password: yup
    .string()
    .required("Please Enter your password")
    .test(
      "regex",
      "Password must be min 8 characters, and have 1 Special Character, 1 Uppercase, 1 Number and 1 Lowercase",
      val => {
        let regExp = new RegExp(
          "^(?=.*\\d)(?=.*[!@#$%^&*])(?=.*[a-z])(?=.*[A-Z]).{8,}$"
        );
        console.log(regExp.test(val), regExp, val);
        return regExp.test(val);
      }
    )

For now, this is working fine. But i would really like to know why the error is popping up. Please post it as an answer if you are able to find a solution, and if it works for me i will mark it as the right answer. Thanks.

You can handle individual error messages as follows.

password: yup.string().min(8, "Must Contain 8 Characters").required()
    .matches(
      /^(?=.*[a-z])/,
      " Must Contain One Lowercase Character"
    )
    .matches(
      /^(?=.*[A-Z])/,
      "  Must Contain One Uppercase Character"
    )
    .matches(
      /^(?=.*[0-9])/,
      "  Must Contain One Number Character"
    )
    .matches(
      /^(?=.*[!@#\$%\^&\*])/,
      "  Must Contain  One Special Case Character"
    ),
yup.addMethod(yup.string, "strongPassword", strongPasswordMethod);

function strongPasswordMethod() {
    return this.test("strongPasswordTest", _, function (value) {
        const { path, createError } = this;
        switch (Boolean(value)) {
            case !/^(?=.*[a-z])/.test(value):
                return createError({ path, message: "password must include lowercase letter" });
            case !/^(?=.*[A-Z])/.test(value):
                return createError({ path, message: "password must include uppercase letter" });
            case !/^(?=.*[0-9])/.test(value):
                return createError({ path, message: "password must include digit" });
            case !/^(?=.*[!@#\$%\^&\*])/.test(value):
                return createError({ path, message: "password must include special character" });
            default:
                return true;
        }
    });
};

const schema = yup.object().shape({
    password: yup.string().required().strongPassword()
});

Came across this last night trying to solve a similar React+Formik+Yup password validation issue. Overall, the solutions here are good. I only comment to offer a slightly different regular expression:

/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[^A-Za-z0-9]).{8,}$/

This differs in the fourth look-ahead. In this version, it looks for anything that is not a letter or digit. The previous REs all limited the definition of "special character" to 8 or so specific characters. This will match a wider range.

I had initially used (?=.*[^\w]), which negates the "word" class (\w), but that class includes the underscore. So doing it that way would not count the underscore as a special character.

None of the above works for me , my solution is here below

  password: yup
  .string()
  .required("Şifreniz kayıt olmak için gereklidir.")
  .matches(
    /^.*(?=.{8,})((?=.*[!@#$%^&*()\-_=+{};:,<.>]){1})(?=.*\d)((?=.*[a-z]){1})((?=.*[A-Z]){1}).*$/,
    "En Az 8 Karakter, Bir Büyük Harf, Bir Küçük Harf, Bir Rakam ve Bir Özel Karakter İçermelidir"
  )

When i searched for this answer, most answers (even the answer marked as the right answer in this thread) I find online is like this, which doesn't check for uppercase characters:

password: yup
    .string()
    .required('Please Enter your password')
    .matches(
      /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/,
      "Must Contain 8 Characters, One Uppercase, One Lowercase, One Number and one special case Character"
    ),

But to me that doesn't actually check if there are both upper and lower case characters. This did work for me though(checks for special char, upper case char, lower case char and number):

password: yup
    .string()
    .required('Please Enter your password')
    .matches(
     /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})/,
      "Must Contain 8 Characters, One Uppercase, One Lowercase, One Number and one special case Character"
    ),

Well considering all of the answers above and after some research, a comprehensive approach can be to check the strength of a password:

password: Yup
    .string()
    .required('Required')
    .matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/,
    "Must Contain 8 Characters, One Uppercase, One Lowercase, One Number and one special case Character"
  )

However, this approach doesn't work for all of the special characters. If you want to include all possible special characters as listed here. This solution can be modified to:

password: Yup
    .string()
    .required('Required')
    .matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~])[A-Za-z\d!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]{8,}$/,
    "Must Contain 8 Characters, One Uppercase, One Lowercase, One Number and one special case Character"
  )

This was the solution I employed in my application. But this is completely your choice, which special characters you want to include/exclude from your password strength check.

I decided to do my own custom validation instead.

Here is what i did :-

password: Yup.string()
      .required("Please Enter your password")
      .min(5, "Your password must be longer than 5 characters.")
      .max(25)
      .matches(/^(?=.{6,})/, "Must Contain 6 Characters")
      .matches(
        /^(?=.*[a-z])(?=.*[A-Z])/,
        "Must Contain One Uppercase, One Lowercase"
      )
      .matches(
        /^(?=.*[!@#\$%\^&\*])/,
        "Must Contain One Special Case Character"
      )
      .matches(/^(?=.{6,20}$)\D*\d/, "Must Contain One Number"),

You can put all that at once like this:

password: Yup
    .string()
    .required('Password is required')
    .matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!+@#\$%\^&\*])(?=.{8,})/, "Must Contain 8 Characters, One Uppercase, One Lowercase, One Number and One Special Case Character"),


confirmPassword: Yup
    .string()
    .required('Confirm password is required')
    .oneOf([Yup.ref('password')], 'Passwords do not match'),

Or you can have them separately like this:

password: Yup
    .string()
    .required('Password is required')
    .matches(/\w*[a-z]\w*/,  "Password must have a small letter")
    .matches(/\w*[A-Z]\w*/,  "Password must have a capital letter")
    .matches(/\d/, "Password must have a number")
    .matches(/[!+@#$%^&*()\-_"=+{}; :,<.>]/, "Password must have a special character")
    .min(8, ({ min }) => `Password must be at least ${min} characters`),


confirmPassword: Yup
    .string()
    .required('Confirm password is required')
    .oneOf([Yup.ref('password')], 'Passwords do not match'),

And you can restrict white spaces like this

.matches(/^\S*$/, "White Spaces are not allowed"),

You can use regex here to validate password strength. With .matches(REGEX HERE) you can add as many condition you want in password.

Example:

password: Yup
    .string()
    .required('Required')
    .matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/,
    "Must Contain 8 Characters, One Uppercase, One Lowercase, One Number and one special case Character"
  )

Ref: https://stackoverflow.com/a/55604455/6270554

This worked for me:

 password: yup
    .string()
    .required('Password is required')
    .matches(regExp)
    .min(6, 'Your password must be longer than 6 characters.')
发布评论

评论列表(0)

  1. 暂无评论