Conditional validation in Yup and formik and semantic-ui - reactjs

I am new to Formik & Yup Validation. I have some fields depend on semantic-ui dropdown .When I click on a field a particular input field and a drop down will open. I cannot do the validation properly. I want when Facebook is selected these fields show validate not Instagram fields, when Instagram is selected only these fields show validation not Facebook fields.. here is the working sandbox link https://codesandbox.io/s/adoring-davinci-z4qlnw?file=/src/UserInfo.jsx My validation code is here..
export const ProfessionalValidation = yup.object().shape({
category: yup.array().required('Category Name is required').nullable(),
FBfollowers: yup.string().required('Atleast One Flatform is required').nullable(),
Instafollowers: yup.string().required('Atleast One Flatform is required').nullable(),
facebookLink: yup.string().required('This Field is required').nullable(),
platform: yup.array().required('Atleast One Flatform is required').nullable(),
})
userinfo is here
import { ProfessionalValidation } from "./Validation";
import { ErrorMessage, Formik, Form, Field } from "formik";
import {
ageGroup,
categoryOptions,
Platform,
gender,
followers,
subscribers
} from "./Data";
import { Dropdown } from "semantic-ui-react";
import "./style.css";
import "./styles.css";
const UserInfo = () => {
const [selectedOptions, setOptions] = useState([]);
const [savedata, setSaveData] = useState([]);
const defaultValue = {
category: "",
platform: "",
facebookLink: "",
InstaLink: "",
Instafollowers: "",
FBfollowers: "",
subscribers: "",
youtube: ""
};
return (
<>
<Formik
initialValues={defaultValue}
validationSchema={ProfessionalValidation}
onSubmit={handleSubmit}
>
{({ values, setFieldValue }) => (
<Form>
<section className="form-styling my-4">
<div className="container">
<div className="input-box2">
<div>
<label htmlFor="category">Category*</label>
<Field
clearable
name={`category`}
fluid
multiple
search
selection
options={categoryOptions}
component={Dropdown}
placeholder="Select Category"
onChange={(_, { value }) =>
setFieldValue("category", value)
}
/>
<span style={{ color: "red" }}>
<ErrorMessage name="category" />
</span>
</div>
<div>
<label htmlFor="platform">Platform*</label>
<Field
clearable
name={`platform`}
fluid
multiple
search
selection
options={Platform}
placeholder="Select Platform"
component={Dropdown}
onChange={(_, { value }) => {
const onDropChange = () => {
setOptions(value);
setSaveData(selectedOptions);
setFieldValue("platform", value);
};
onDropChange();
}}
//onChange= {onDropChange}
/>
<span style={{ color: "red" }}>
<ErrorMessage name="platform" />
</span>
<span className="mt-3 d-block">Select at least 1</span>
</div>
</div>
{/* all selected platform show in this div */}
<div className="platform-box">
<div>
{selectedOptions.find((word) => word === "Facebook") &&
values.platform.find((word) => word === "Facebook") ? (
<div>
<div className="platform-details">
<div>
<h5>Facebook</h5>
</div>
<div>
<Field
type="text"
name="facebookLink"
placeholder="Account Link"
//onBlur={(value) => setFieldValue("facebookLink",value.currentTarget)}
/>
<span className="text-danger">
<ErrorMessage name="facebookLink" />
</span>
</div>
<div>
<Field
className="label ui selection fluid dropdown"
clearable
name={`FBfollowers`}
fluid
search
selection
options={followers}
component={Dropdown}
placeholder="Select followers"
onChange={(_, { value }) =>
setFieldValue("FBfollowers", value)
}
/>
<span className="text-danger">
<ErrorMessage name="FBfollowers" />
</span>
</div>
</div>
<span>Verify the Followers in 2-3 Business Days</span>
</div>
) : null}
{selectedOptions.find((word) => word === "Instagram") ? (
<div>
<div className="platform-details">
<div>
<h5>Instagram</h5>
</div>
<div>
<Field
type="text"
name="InstaLink"
placeholder="Account Link"
/>
</div>
<div>
<Field
className="label ui selection fluid dropdown"
clearable
name={`Instafollowers`}
fluid
search
selection
options={followers}
component={Dropdown}
placeholder="Select followers"
onChange={(_, { value }) =>
setFieldValue("Instafollowers", value)
}
/>
<span style={{ color: "red" }}>
<ErrorMessage name="Instafollowers" />
</span>
</div>
</div>
<span>Verify the Followers in 2-3 Business Days</span>
</div>
) : null}
</div>
</div>
</div>
{/* container end */}
</section>
{/* additional info */}
<section className="additional-Info ">
<div className="container">
<div className="main-box"></div>
<div className="text-start my-5">
<button type="submit" className="border-0">
<span className="btn-form btn btn-info">Continue</span>
</button>
</div>
</div>
</section>
</Form>
)}
</Formik>
</>
);
};
export default UserInfo;

Yup provides .when api for mixed validation.
You can use it like below.
FBfollowers: yup
.string()
.when("platform", {
is: (val) => val?.includes("Facebook"),
then: yup.string().required("Atleast One Flatform is required"),
otherwise: yup.string()
})
.nullable(),
Since the value for platform field is an array, I've used .includes to check whether user has selected specific platform. If this returns true then the field will be made mandatory, otherwise it will be non-mandatory.
Here is updated code for validation.jsx file.
import * as yup from "yup";
export const ProfessionalValidation = yup.object().shape({
category: yup.array().required("Category Name is required").nullable(),
FBfollowers: yup
.string()
.when("platform", {
is: (val) => val?.includes("Facebook"),
then: yup.string().required("Atleast One Flatform is required"),
otherwise: yup.string()
})
.nullable(),
Instafollowers: yup
.string()
.when("platform", {
is: (val) => val?.includes("Instagram"),
then: yup.string().required("Atleast One Flatform is required"),
otherwise: yup.string()
})
.nullable(),
facebookLink: yup
.string()
.when("platform", {
is: (val) => val?.includes("Facebook"),
then: yup.string().required("This field is required"),
otherwise: yup.string()
})
.nullable(),
platform: yup.array().min(1, "Atleast one Field is required").nullable()
});
And here is the updated working code sandbox.
Here is more on the .when api of Yup.
Hope this helps.

Related

react select mult with formik, the value does not appear in the array

Hello I have a web application, and I need to put a multi select in a modal, on the front it appears as it should, but I can't return its value in the input correctly as an array, in case anyone can help me. I'm using formik to manage submission in submit
the value of the second field works as expected, I've tried using useState to insert the value in the array but it didn't work either
Example of how values ​​are returned:
alert values
example of how values ​​are expected
example
import clsx from 'clsx'
import Axios from "axios";
import React, { useState } from "react";
import 'react-notifications-component/dist/theme.css'
import { Store } from 'react-notifications-component';
import { useFormik, FieldProps, FieldArray } from 'formik';
import Select , { OptionsType , ValueType } from "react-select" ;
import { Options, OnChangeValue } from "react-select";
const Modal_cadastra = ({close}) => {
const cities = [
{label: 'New York1', value: 'NY'},
{label: 'Rome', value: 'RM'},
{label: 'London', value: 'LDN'},
{label: 'Istanbul', value: 'IST'},
{label: 'Paris', value: 'PRS'}
];
const formik = useFormik({
initialValues: {
atividades: {
atividades_id:[],
},
descricao: '',
},
onSubmit: values => {
alert(JSON.stringify(values, null, 2));
},
});
return (
<>
<form id='kt_modal_add_user_form' onSubmit={formik.handleSubmit} className='form'noValidate>
<div
className='modal fade show d-block modal-open'
id='kt_modal_add_user'
role='dialog'
tabIndex={-1}
aria-modal='true'
>
{/* begin::Modal dialog */}
<div className='modal-dialog modal-dialog-centered mw-650px'>
{/* begin::Modal content */}
<div className='modal-content'>
<div className='modal-header'>
{/* begin::Modal title */}
<h2 className='fw-bolder'>Cadastrar tipo mão de obra</h2>
<div
className='btn btn-icon btn-sm btn-active-icon-primary'
data-kt-users-modal-action='close'
style={{cursor: 'pointer'}}
>
</div>
</div>
<div className='modal-body scroll-y mx-5 mx-xl-15 my-7'>
<div className='col-lg-10 fv-row'>
<Select
defaultValue={formik.values.atividades.atividades_id}
onBlur={formik.handleChange}
isMulti
name="atividades_id"
id="atividades_id"
options={cities}
className="basic-multi-select"
classNamePrefix="select"
/>
</div>
<div className='fv-row mb-7'>
<label className='fw-bold fs-6 mb-2'>TIPO MÃO DE OBRA</label>
<input
placeholder='Tipo mão de obra'
type='text'
name='descricao'
id='descricao'
className={clsx(
'form-control form-control-solid mb-3 mb-lg-0',
)}
autoComplete='off'
disabled={false}
onChange={formik.handleChange}
value={formik.values.descricao}
/>
</div>
</div>
<div className='fv-row mb-7'>
<div className='text-center'>
<button
type='reset'
className='btn btn-light me-3'
data-kt-users-modal-action='cancel'
onClick={ close }
>
Sair
</button>
<button
type='submit'
className='btn btn-primary'
data-kt-users-modal-action='submit'
>
<span className='indicator-label'>Salvar</span>
</button>
</div>
</div>
</div>
</div>
</div>
<div className='modal-backdrop fade show'></div>
</form>
</>
)
}
export {Modal_cadastra}
This is because Formik checks the actual rendered HTML tag for the name and react-select is not passing the name to the HTML element. It ignores it and uses its own.
Generally you need to bind third party components. You could split this out and then use it in multiple places:
<Field name="atividades_id">
{({ field, form, meta }) => (
<Select
{...field}
isMulti
id="atividades_id"
onChange={(values) => form.setFieldValue("atividades_id", values)} {/* Because formiks field.onChange` accepts an event, we need to manually bind this one as Select passes up the new values not nested in event */}
getOptionValue={option => cities.value}
options={cities}
className="basic-multi-select"
classNamePrefix="select"
/>
)}
</Field>

Error while creating validations for react-hook-form

I am trying to create validations for my react-hook-form but when I go try to display the errors on the front end I receive this error.
×
TypeError: Cannot read properties of undefined (reading 'name')
It seems like everything is working until I try to display it with the following code
{errors.name && errors.name.message}
import emailjs from "emailjs-com";
import { useForm } from "react-hook-form";
const Contact = () => {
const [successMessage, setSuccessMessage] = useState("");
const { register, handleSubmit, errors } = useForm();
const serviceID = "s_ID";
const templateID = "t_ID";
const userID = "user_12345";
const onSubmit = (data, r) => {
sendEmail(
serviceID,
templateID,
{
name: data.name,
email: data.email,
subject: data.subject,
description: data.description,
},
userID
)
r.target.reset();
}
const sendEmail = (serviceID, templateID, variables, userID) => {
emailjs.send(serviceID, templateID, variables, userID)
.then(() => {
setSuccessMessage("Form sent successfully! I'll contact you as soon as possible");
}).catch(err => console.error(`Something went wrong ${err}`));
};
return (
<div className="contacts">
<div className="text-center">
<h1 className="bold upper-case">contact me</h1>
</div>
<div className="container">
<form onSubmit={handleSubmit(onSubmit)}>
<div className="row">
<div className="col-md-6 col-xs-12">
{/* Name Input */}
<input type="text" className="form-control" placeholder="Name" name="name"
{
...register("name", {
required: "Please enter your name",
maxLength: {
value: 20,
message: "Name must not be longer than 20 characters",
}
})
} />
<span className="error-message">
<p> {errors.name && errors.name.message} </p>
</span>
{/* Email Input */}
<input type="email" className="form-control" placeholder="E-mail" name="email" />
{/* Subject Input */}
<input type="text" className="form-control" placeholder="Subject" name="subject" />
</div>
<div className="col-md-6 col-xs-12">
{/* Subject Input */}
<textarea type="text" className="form-control" placeholder="Subject" name="description" />
<div className="text-center center">
<button className="btn btn-outline-dark contact-button p-button mt-3" type="submit">
Send!
</button>
</div>
</div>
</div>
</form>
</div >
</div >
)
}
export default Contact
Your code helped me debug my EmailJS submission so it's only right if I try my best to help you.
I'm able to successfully display my validation error messages by doing the following:
const {
register,
handleSubmit,
formState: { errors },
} = useForm<IContactForm>();
.
.
.
<form>
<input {...register("firstName", { required: "ENTER YOUR FIRST NAME" })}/>
{errors.firstName && errors.firstName.message}
</form>
Only difference I see is that you don't have 'formState: {errors}' for your useForm hook.

Yup required validation not working and not giving error for empty field

I'm working on a form using formik and yup. I have added required schema, but it is not working. I can easily save having empty input fields. I have tried and googled but nothing worked.
I want to make it mandatory and it should give error if field is empty.
snippet of yup schema validation
opening_time: Yup.string().required("Opening time is Requried"),
closing_time: Yup.string().required("Closing time is Requried"),
address: Yup.string().required("Address is Requried"),
about: Yup.string().required("About is Required"),
Input field snippet
<div class="form-group mb-0">
<label>
About<span className="text-danger">*</span>
</label>
<textarea
name="about"
onChange={formik.handleChange}
value={formik.values.about}
class="form-control"
rows="5"
required
/>
{formik.touched.about && formik.errors.about ? (
<div className="err">
{formik.errors.about}
{console.log(formik.errors.about)}
</div>
) : null}
</div>
Try the following:
import React from 'react';
import { Formik, Form, Field } from 'formik';
import * as Yup from 'yup';
const SignupSchema = Yup.object().shape({
opening_time: Yup.string().required("Opening time is Requried"),
closing_time: Yup.string().required("Closing time is Requried"),
address: Yup.string().required("Address is Requried"),
about: Yup.string().required("About is Required"),
});
function ValidationSchemaExample() {
function updateDoctorProfile(e, values) {
console.log(`e: ${e}`);
console.log(`values: ${values}`)
}
return (
<div>
<h1>Signup</h1>
<Formik
initialValues={{
opening_time: "",
closing_time: "",
address: "",
about: "",
}}
validationSchema={SignupSchema}
onSubmit={values => {
// same shape as initial values
console.log(values);
}}
>
{({ values, errors, touched, handleChange, handleSubmit, isSubmitting }) => (
< div className="form-group mb-0">
<label>
About<span className="text-danger">*</span>
</label>
<textarea
name="about"
onChange={handleChange}
value={values.about}
className="form-control"
required
/>
<button type="submit" onClick={(e) => {
handleSubmit();
updateDoctorProfile(e, values);
}} disabled={isSubmitting}>
Submit
</button>
{touched.about && errors.about ? (
<div className="err">
{errors.about}
{console.log(errors.about)}
</div>
) : null}
</div>
)}
</Formik>
</div >
);
}
export default ValidationSchemaExample;
The only change is that the button tag's onClick attribute is passed the handleSubmit function along with your updateProfile function.

In ReactJs using Formik library to handle form & Yup library

In ReactJs using formik library to handle form & Yup library for form validation rule, as i am trying to use "yup.array().of()" validator to validate array of fields (name, gender) which are passed from other component through props and i have stored this components in another component using accordion which is like the stepper, but it isn't working for me as i am not able to get the validation.
function FormBrowserG(props) {
const { intl } = props;
const peopleArray = props.peopleArrayValue;
//initial values
const initialValues = {
people: []
}
//creating the validation schema
const validationSchema = Yup.object().shape({
people: Yup.array().of(
Yup.object().shape({
name: Yup.string()
.required('Name is required'),
gender: Yup.string()
.required('gender is required')
})
)
});
//using useFormik
const formik = useFormik({
initialValues,
validationSchema,
onSubmit: (values) => {
}
});
const people = []
for (let index = 0; index < props.numberOfPeopleValue; index++) {
people.push(
<fieldset className="fieldset">
<legend className="legend">Person</legend>
<div className="display-flex">
<div className="formname">
<label className="gendername"><FormattedMessage id="AUTH.GENDER.LABEL" /></label>
<select
{...formik.getFieldProps('gender')}
className="form-control">
<option>Select</option>
<option key={10}>Male</option>
<option key={20}>Female </option>
</select>
{formik.touched.gender && formik.errors.gender ? (
<div className="warnings">{formik.errors.gender}</div>
) : null}
</div>
<div className="formo">
<label className="gendername"><FormattedMessage id="AUTH.SIGN_UP.NAME.LABEL" /></label>
<input
type="text"
id={`name${index}`}
className="name"
{...formik.getFieldProps(`name${index}`)}
required
/>
{formik.touched.name && formik.errors.name ? (
<div className="warnings">{formik.errors.name}</div>
) : null}
</div>
</div>
</fieldset>
)
}
return (
<>
<form onSubmit={formik.handleSubmit}>
<div className="row">
{people}
</div>
</form>
<div className="bofferingbtn">
<Link to={{
pathname: "/Confirmation",
state: value
}}>
<Button variant="contained" type="submit" className="obtn" size="large" color="primary" >
<FormattedMessage id="AUTH.OFFERING.BUTTON" />
</Button>
</Link>
</div>
</>
)
}
export default injectIntl(FormBrowserG)
You need to pass your values to formik like this -
initialValues={{ name: "", gender: "" }}
onSubmit={...}
//this will automatically validate the field
validationSchema={validationSchema}

How to add Radio Button in Formik Validations Reactjs?

I am using Formik for Validating my Registration form I want to add validate gender via radio button how can I do that. I am not able to add radio button.
This is what I have done:-
const SignupSchema = Yup.object().shape({
email: Yup.string()
.email('Invalid email')
.required('Required'),
password: Yup.string()
.min(4, 'Password Must be four characters long!')
.max(20, 'Too Long!')
.required('Required'),
});
class Register extends Component {
render() {
return (
<Formik
initialValues={{
email: '',
password:'',
gender:'',
}}
validationSchema={SignupSchema}
onSubmit={values => {
console.log(values);
}}
>
{({ errors, touched }) => (
<Form>
<Field style={customStyles.textfield} placeholder="Email" name="email" type="email" />
{errors.email && touched.email ? <div}>{errors.email}</div> : null}
<Field placeholder="Enter Password" name="password" type="password" />
{errors.password && touched.password ? <div >{errors.password}</div> : null}
<button type="submit">Submit</button>
</Form>
)}
</Formik>
</div>
)
}
}
change gender initialValues as male
<Field
name="gender"
render={({ field }) => (
<>
<div className="radio-item">
<input
{...field}
id="male"
value="male"
checked={field.value === 'male'}
name="type"
type="radio"
/>
<label htmlFor="male">Male</label>
</div>
<div className="radio-item">
<input
{...field}
id="female"
value="female"
name="type"
checked={field.value === 'female'}
type="radio"
/>
<label htmlFor="female">Female</label>
</div>
</>
)}
/>
This solution gives you the chance to have more than 2 options.
Let say you have a file called Main.js and you want to put a radio button input in it. First, provide a list of options for your radio button in the Main.js
const radioOptions = [
{ key: 'Option 1', value: 'value 1' },
{ key: 'Option 2', value: 'value 2' },
{ key: 'Option 3', value: 'value 3' }
];
Next, create a RadioButton.js file with below code
import React from 'react';
import { Field } from 'formik';
const RadioButton = (props) => {
const { label, name, options, ...rest } = props;
return (
<div>
<label htmlFor={name}>{label}</label>
<Field name={name} {...rest} >
{
({ field }) => {
return options.map(option => {
return (
<React.Fragment key={option.key}>
<input
type='radio'
id={option.id}
{...field}
value={option.value}
checked={field.value === option.value}
/>
<label htmlFor={option.id}>{option.key}</label>
</React.Fragment>
);
})
}
}
</Field>
</div>
);
};
export default RadioButton;
Then put the reusable RadioButton Component in the Main.js wherever you want the radio button input to render.
The result UI will be 3 radio buttons with values "value 1", "value 2" and "value 3".
You can check out this awesome youtube series to know more.
This can be done simply but using this code below. it worked for me:
You will need to import Formik from "formik"
<Formik
initialValues={{
email: '',
password:'',
gender:'',
}}
// validations
validationSchema={SignupSchema}
onSubmit={values => { console.log(values); }}
>
{(formik) => (
<Form onSubmit={formik.handleSubmit}>
//Your Other inputs........
<div className="custom-control">
<input
id="male"
type="radio"
value="male"
name='gender'
onChange={formik.handleChange}
defaultChecked={formik.values.gender=== "male"}
/>
<label
className="custom-control-label"
htmlFor="male"
>
Male
</label>
</div>
<div className="custom-control">
<input
id="female"
type="radio"
value="female"
name='gender'
onChange={formik.handleChange}
defaultChecked={formik.values.gender=== "female"}
/>
<label
className="custom-control-label"
htmlFor="female"
>
Female
</label>
</div>
//Your Other inputs......
<button type="submit">Submit</button>
</Form>
)}
</Formik>

Resources