Jiren97
Jiren97

Reputation: 13

React: Yup conditional validation only when a field is visible

I'm trying to implement form validation using formik & yup in React. I have a login/register form with three fields (name,email,password). Name field is conditionally rendered when 'create an account' button is clicked. I want the name field to be required only when form is in register state. I'm using a state variable isLogin to save the form's current state, also using it to initialise showName boolean in formik's initialValues. Right now, name field has no validation applied on it and form can be submitted if name field is empty.

My Code

const [isLogin, setIsLogin] = useState(true);

const initialAuthFormValues = {
  hideName: isLogin, 
  name: "",
  email: "",
  password: "",
};

My Validation Schema

const authFormValidationSchema = Yup.object().shape({
  hideName: Yup.boolean(),

  name: Yup.string().when("hideName",{
  is: false,
  then: Yup.string().required("Name is required"),
  }),

 email: Yup.string().required("Email is required"),
 password: Yup.string().required("Password is required"),
});

My Component looks like this

<Formik
  initialValues={initialAuthFormValues}
  validationSchema={authFormValidationSchema}
  onSubmit={submitHandler}
>
  {(formik) => (
      <Form>
        {!isLogin && (
          <TextField
            label="Name"
            type="text"
            name="name"
            placeholder="Name"
          />
        )}
        <TextField
          label="Email"
          type="text"
          name="email"
          placeholder="Email"
        />
        <TextField
          label="Password"
          type="password"
          name="password"
          placeholder="Password"
        />
        <div className={classes.actions}>
          
          <Button type="submit"> {isLogin ? "Login" : "Create Account"} </Button>
          
          <Button
            type="reset"
            className="toggle"
            onClick={() => { setIsLogin((prevState) => !prevState); }}
          >
            {isLogin ? "Create new account" : "Login with existing account"}
          </Button>
        </div>
      </Form>
    </section>
  )}
</Formik>

Upvotes: 1

Views: 1904

Answers (2)

Zach Bkh
Zach Bkh

Reputation: 126

Finally found the solution to the problem : In my code, action is my prop

  1. First, create a variable in your JS that will hold the logic:
let passwordTest
      
if (action === "Register") {
  passwordTest = {
    password2: Yup
      .string().trim()
      .required("Please confirm your password")
      .oneOf([Yup.ref('password'), null], 'Passwords must match')
} else {
   passwordTest = null
}

Basically above you are saying that if the props is Register (if this form is rendered for registering purpose and not login), then create the field apply the validation listed, otherwise, this field will be null.

  1. Then, in your Yup schema destructure your variable :
const validationSchemaYup = Yup.object().shape({

other fields validation logic...
...passwordTest,
other fields validation logic...

Here you add your new custom validation logic, don't forget to destructure!

Upvotes: 0

Rawand Deheliah
Rawand Deheliah

Reputation: 1602

You can use authFormValidationSchema as a state , and update this state in case that button clicked :

const authFormValidationSchema = {
 email: Yup.string().required("Email is required"),
 password: Yup.string().required("Password is required"),
};
const [authFormValidationSchema, setAuthFormValidationSchema] = useState(Yup.object());

track the status when button is clicked in a useEffect, and update state of authFormValues accordingly :

useEffect(()=>{
  let authFormValidationSchema_ = JSON.parse(JSON.stringify(authFormValidationSchema))
  if(!buttonIsClicked)
  setAuthFormValidationSchema(Yup.object(authFormValidationSchema_));
  else  setAuthFormValidationSchema(Yup.object({...authFormValidationSchema_, name: Yup.string().required("Name is required") }));
  },[buttonIsClicked]);

Upvotes: 0

Related Questions