0
votes

I'm trying to validate a confirm password value with yup. Every solution I've looked through points to the same line of code but it's not working as intended.

I have a form with two inputs (password and confirm), and one submit button. The submit button is disabled until the formValues state passes validation. When the passwords don't match, the submit button is correctly disabled and when they match the button becomes active. However the error message does not go away.

Here is my yup schema:

import * as yup from 'yup'

export default yup.object().shape({
  password: yup
    .string()
    .required("Password required"),
  confirm: yup
    .string()
    .oneOf([yup.ref("password"), null], "Passwords don't match")
})

and here is the code for the form itself:

const initialValue = {
  password: "",
  confirm: ""
}

export default function Form() {
const [formValues, setFormValues] = useState(initialValue)
  const [formErrors, setFormErrors] = useState(initialValue)
  const [disabled, setDisabled] = useState(true)

  const onChange = (e) => {
    const {name, value} = e.target;
    yup
      .reach(formSchema, name)
      .validate(value)
      .then(() => {
        setFormErrors({ ...formErrors, [name]: '' })
      })
      .catch((err) => {
        setFormErrors({ ...formErrors, [name]: err.errors[0] })
      })
    
    setFormValues({...formValues, [name]: value })
  }

  const onSubmit = (e) => {
    e.preventDefault()
  }

  useEffect(() => {
    formSchema.isValid(formValues).then((valid) =>
    setDisabled(!valid))
  }, [formValues])

  return (
    <div>
      <form onSubmit={onSubmit}>
        <label>
          Password:
          <input
            type="text"
            name="password"
            onChange={onChange}
            value={formValues.password}
          />
        </label>
        <label>
          Confirm Password:
          <input
            type="text"
            name="confirm"
            onChange={onChange}
            value={formValues.confirm}
          />
        </label>
        <button type="submit" disabled={disabled}>
          Submit
        </button>

        <div className="errors">
          <div>{formErrors.password}</div>
          <div>{formErrors.confirm}</div>
        </div>
      </form>
    </div>
  )
}
1

1 Answers

0
votes

First of all each time you checked old formValues. Second you should check touched fields for show error messages after user effected them. I suggest to use Formik.

But I fixed your code and also you can check it on this

import React, { useState, useEffect } from "react";
import * as yup from "yup";

const initialValue = {
  password: "",
  confirm: ""
};

export default function Form() {
  const [formValues, setFormValues] = useState(initialValue);
  const [formErrors, setFormErrors] = useState(initialValue);
  const [touched, setTouched] = useState({password: false, confirm: false});
  const [disabled, setDisabled] = useState(true);

  const formSchema = yup.object().shape({
    password: yup.string().required("Password required"),
    confirm: yup
      .string()
      .oneOf([yup.ref("password"), null], "Passwords don't match")
  });

  const onChange = (e) => {
    const { name, value } = e.target;

    setFormValues({ ...formValues, [name]: value });
    setTouched({...touched, [name]: true});
  };

  const onSubmit = (e) => {
    e.preventDefault();
  };

  useEffect(() => {
    yup
      .reach(formSchema)
      .validate(formValues, { abortEarly: false })
      .then(() => {
        setFormErrors({});
      })
      .catch((err) => {
        const errors = {};
        err.inner.forEach(error => {
          if(touched[error.path]) errors[error.path] = error.message;
        })
        setFormErrors(errors);
      });

    formSchema.isValid(formValues).then(valid => setDisabled(!valid));
  }, [formValues]);

  return (
    <div>
      <form onSubmit={onSubmit}>
        <label>
          Password:
          <input
            type="text"
            name="password"
            onChange={onChange}
            value={formValues.password}
          />
        </label>
        <label>
          Confirm Password:
          <input
            type="text"
            name="confirm"
            onChange={onChange}
            value={formValues.confirm}
          />
        </label>
        <button type="submit" disabled={disabled}>
          Submit
        </button>

        <div className="errors">
          <div>{touched.password && formErrors.password}</div>
          <div>{touched.confirm && formErrors.confirm}</div>
        </div>
      </form>
    </div>
  );
}