Formik submitting a form with empty field - reactjs

I have below form with Formik and Yup Validation -
<Formik
initialValues={{
email: '',
address: '',
password: ''
}}
validationSchema={ Validation }
onSubmit={values => {
// same shape as initial values
console.log(values);
}}>
{({ errors, status, touched }) => (
<Form>
<FormGroup>
<Label for="exampleEmail">Email</Label>
<Field name="email" type={'email'} component={customInputForm}/>
</FormGroup>
<FormGroup>
<Label for="address">Address</Label>
<Field name="address" type={'text'} component={customInputForm}/>
</FormGroup>
<FormGroup>
<Label for="examplePassword">Password</Label>
<Field name="password" type={'password'} component={customInputForm}/>
</FormGroup>
<Button>Submit</Button>
</Form>
)}
</Formik>
Here when I click on Submit button with empty field form is submitting without any error but when I touch the input then it is giving error. Not sure what I am doing wrong here I want to check validation on Submit Button Click also.
Below is Yup Error Message -
const Validation = Yup.object().shape({
address: Yup.string()
.min(2, "Too Short!")
.max(50, "Too Long!")
.required("Required"),
password: Yup.string()
.min(2, "Too Short!")
.max(50, "Too Long!")
.required("Required"),
email: Yup.string()
.email("Invalid email")
.required("Required")
});
export default Validation;

Related

Can't submit multiple step form with yup validations and react-hook-form

I am having troubles with react-hook-form and yup, I need to create a multistep form (there are many other inputs but I just leave a few to give you an example), so!
the stepper works fine until the finish step, when I click "Register" (last step, should submit) it shows the following error:
here is my code:
const RegisterClient = props => {
const [currStep, setCurrStep] = React.useState(1)
const schema1 = Yup.object().shape({
// company
company_name: Yup.string().required("Name required"),
email: Yup.string().email().required("Email required"),
password: Yup.string()
.matches(
passwordRules,
"Password must contain at least 8 digits (one uppercase, one lowercase, one number and a symbol)."
)
.required("Required"),
})
const scheme2 = Yup.object().shape({
// representative
representative_name: Yup.string().required(
"Representative name is required"
),
representative_email: Yup.string()
.email()
.required("Representative email required"),
})
const {
control,
handleSubmit,
formState: { errors },
getValues,
} = useForm({
resolver: yupResolver(currStep === 1 ? schema1 : currStep === 2 && scheme2),
defaultValues: {
company_name: "",
email: "",
password: "",
representative_name: "",
representative_email: "",
},
})
React.useEffect(() => {
console.log(errors)
}, [errors])
function stepper() {
const steps = {
1: <Step1 control={control} errors={errors} />,
2: <Step2 control={control} errors={errors} />,
3: <FinalStep control={control} errors={errors} />,
}
return steps[currStep]
}
function previousStep() {
console.log("previous...")
if (currStep === 1) return
setCurrStep(curr => curr - 1)
}
function nextStep() {
console.log("next: ", getValues())
setCurrStep(curr => curr + 1)
return
}
const onSubmit = async user => {
if (currStep < 3) nextStep()
else {
console.log("user: ", user)
}
}
return (
<React.Fragment>
<Form className="form-horizontal" onSubmit={handleSubmit(onSubmit)}>
{stepper()}
<div className="mt-3 d-flex gap-4 justify-content-between">
<Button
outline
color="dark"
type="button"
onClick={previousStep}
disabled={currStep === 1}
>
Previous
</Button>
<Button color="primary">
{loading ? (
<React.Fragment>
Loading... <Spinner size="sm">Loading...</Spinner>
</React.Fragment>
) : currStep === 3 ? (
"Register"
) : (
"Next"
)}
</Button>
</div>
<div className="mt-4 text-center">
<p className="mb-0">
By registering you agree to the Skote{" "}
<Link to="#" className="text-primary">
Terms of Use
</Link>
</p>
</div>
</Form>
</React.Fragment>
)
}
export default RegisterClient
function Step1({ control, errors }) {
return (
<React.Fragment>
<FormGroup>
<Label for="company_name">Company Name</Label>
<Controller
name="company_name"
control={control}
render={({ field }) => (
<Input
{...field}
placeholder="Company Name"
type="text"
invalid={Boolean(errors.company_name)}
/>
)}
/>
{errors.company_name && (
<FormFeedback>{errors.company_name?.message}</FormFeedback>
)}
</FormGroup>
<Row>
<Col sm="12" md="6" lg="6" xl="6">
<FormGroup>
<Label for="email">Email</Label>
<Controller
name="email"
control={control}
render={({ field }) => (
<Input
{...field}
placeholder="Email"
type="email"
invalid={Boolean(errors.email)}
/>
)}
/>
{errors.email && (
<FormFeedback>{errors.email?.message}</FormFeedback>
)}
</FormGroup>
</Col>
<Col sm="12" md="6" lg="6" xl="6">
<FormGroup>
<Label for="password">Password</Label>
<Controller
name="password"
control={control}
render={({ field }) => (
<Input
{...field}
placeholder="Password"
type="password"
invalid={Boolean(errors.password)}
/>
)}
/>
{errors.password && (
<FormFeedback>{errors.password?.message}</FormFeedback>
)}
</FormGroup>
</Col>
</Row>
</React.Fragment>
)
}
function Step2({ control, errors }) {
return (
<React.Fragment>
<FormGroup>
<Label for="representative_name">Representative name</Label>
<Controller
name="representative_name"
control={control}
render={({ field }) => (
<Input
{...field}
placeholder="Representative name"
type="text"
invalid={Boolean(errors.representative_name)}
/>
)}
/>
{errors.representative_name && (
<FormFeedback>{errors.representative_name?.message}</FormFeedback>
)}
</FormGroup>
<FormGroup>
<Label for="representative_email">Representative email</Label>
<Controller
name="representative_email"
control={control}
render={({ field }) => (
<Input
{...field}
placeholder="Representative email"
type="text"
invalid={Boolean(errors.representative_email)}
/>
)}
/>
{errors.representative_email && (
<FormFeedback>{errors.representative_email?.message}</FormFeedback>
)}
</FormGroup>
</React.Fragment>
)
}
function FinalStep() {
return <h2>This is the final step...</h2>
}
My second option is to have a single schema like this:
const schema = Yup.object().shape({
company: Yup.object().shape({
// company
company_name: Yup.string().required("Name required"),
email: Yup.string().email().required("Email required"),
password: Yup.string()
.matches(
passwordRules,
"Password must contain at least 8 digits (one uppercase, one lowercase, one number and a symbol)."
)
.required("Required"),
}),
representative: Yup.object().shape({
// representative
representative_name: Yup.string().required(
"Representative name is required"
),
representative_email: Yup.string()
.email()
.required("Representative email required"),
})
})
but in this case, I have no idea how to make react-hook-form check for the specific schema inside the main one, am I explain?
I need help please :(
Well, I'm not the guy who just wait for responses... I already figure it out, so here's my solution if someone has the same issue in the future:
const schema = [
Yup.object().shape({
// company
company_name: Yup.string().required("Name required"),
description: Yup.string().required("Description required"),
phone: Yup.string().required("Phone required"),
email: Yup.string().email().required("Email required"),
validateEmail: Yup.string()
.email()
.required("Please confirm your email")
.oneOf([Yup.ref("email")], "Email do not match"),
password: Yup.string()
.matches(
passwordRules,
"Password must contain at least 8 digits (one uppercase, one lowercase, one number and a symbol)."
)
.required("Required"),
validatePassword: Yup.string()
.required("Please confirm your password")
.oneOf([Yup.ref("password")], "Passwords do not match"),
}),
Yup.object().shape({
// representative
representative_name: Yup.string().required(
"Representative name is required"
),
representative_position: Yup.string().required(
"Representative position is required"
),
representative_email: Yup.string()
.email()
.required("Representative email required"),
representative_phone: Yup.string().required("Phone required"),
}),
Yup.object().shape({
line_business: Yup.string().required(),
status: Yup.string().required(),
user_type: Yup.string().required(),
}),
]
const {
control,
handleSubmit,
formState: { errors },
getValues,
} = useForm({
resolver: yupResolver(schema[currStep - 1]), // this is because my steps start with 1, and the schema array start with position 0
defaultValues: {
company_name: "",
description: "",
phone: "",
email: "",
validateEmail: "",
password: "",
validatePassword: "",
representative_name: "",
representative_position: "",
representative_email: "",
representative_phone: "",
line_business: "1",
status: "2",
user_type: userType,
},
})
NOTICE: that in my case, the last object in the schema array check validation for values that I have defined with default values because I don't need to change those, I think that if you just add any validation without the .required() should work as well 🤷🏻‍♂️

How to get the Yup conditional validation to work with Formik in Reactjs?

I have a Formik form that has two radio buttons that would ask the user to choose if he is a staff member or a student and based on his selection then I will be able to show different fields for the user to fill out using Yup validation.
I am not sure how to use the when() method here !!
Here is my Formik
<Formik
initialValues ={{
name: '',
school: '',
jobType: '',
parentName: '',
parentPhone: '',
parentEmail: '',
staffPhone:'',
staffEmail: '',
condition:'',
}}
onSubmit={async (values) => {
await new Promise((r) => setTimeout(r, 500));
alert(JSON.stringify(values, null, 2));
}}
validationSchema={validate}
>
I am trying to use Yup validation to show the fields based on the users choice
my validation Schema
const validate = Yup.object({
name: Yup.string()
.max(20, "Must be 20 characters or less" )
.required('required'),
school: Yup.string().required('Please select your school').oneOf(schools),
jobType: Yup.string().required("are you a student or a staff member ?!"),
parentName: Yup.string().when('jobType', {
is: true ,
then: Yup.string().required('Field is required')
}),
as for the form itself :
<div id="my-radio-group">Job type</div>
<ErrorMessage name="jobType" />
<div role="group" aria-labelledby="my-radio-group">
<label>
<Field type="radio" name="jobType" value="student" />
Student
</label>
<label>
<Field type="radio" name="jobType" value="staff member" />
Staff member</label>
{console.log({values})}
{/* <div>Picked: {values.jobType}</div> */}
</div>
<button className="btn btn-dark mt-3" type="submit"> Register</button>
You just need to do some conditional rendering of the fields you want based on the value the jobType.
The jobType is expected to be a string that can take on one of the two values "staff member" or "student" depending on the radio selected.
In your case, jobType is being checked against a boolean value true which is wrong. This should be checked against either of the two strings "student" or "staff member".
You should change your validation schema to something like this:
const validate = Yup.object({
name: Yup.string()
.max(20, "Must be 20 characters or less")
.required("Required!"),
school: Yup.string()
.required("Please select your school")
.oneOf(schools),
jobType: Yup.string().required("Are you a student or a staff member?"),
parentName: Yup.string().when('jobType', {
is: "student",
then: Yup.string().required("Field is required")
}),
parentEmail: Yup.string().when('jobType', {
is: "student",
then: Yup.string().required("Field is required")
}),
staffPhone: Yup.string().when('jobType', {
is: "staff member",
then: Yup.string().required("Field is required")
}),
staffEmail: Yup.string().when('jobType', {
is: "staff member",
then: Yup.string().required("Field is required")
})
})
When rendering your form it should be dependent on the values in the jobType like so:
{
values.jobType === "student"
&&
<>
<label>
Parent Name
<Field type="text" name="parentName" placeholder="Enter name" />
</label>
<ErrorMessage name="parentName"/>
<label>
Parent Email
<Field type="text" name="parentEmail" placeholder="Enter name" />
</label>
<ErrorMessage name="parentEmail"/>
</>
}
{
values.jobType === "staff member"
&&
<>
<label>
StaffPhone
<Field type="text" name="staffName" placeholder="Enter name" />
</label>
<ErrorMessage name="staffName"/>
<label>
StaffEmail
<Field type="text" name="staffEmail" placeholder="Enter name" />
</label>
<ErrorMessage name="staffEmail"/>
</>
}
You can also checkout the entire code in my
sandbox here.

Why isn't my Formik form showing warning message? React, Formik

I'm trying to get some basic front end form validation done using Formik. I've validated the user but the error message is not showing on my front end.
I am looking to display an error message iF my form does not meet my validation schema.
My validation schema:
import * as yup from 'yup';
export const userSignUpSchema = yup.object().shape({
name: yup
.string()
.min(4, "Too short")
.max(20, "Too long")
.required("Required"),
email: yup
.string()
.email("Invalid email")
.required("Required"),
password: yup
.string()
.min(4, "Too short")
.max(20, "Too long")
.required("Required"),
confirmPassword: yup
.string()
.min(4, "Too short")
.max(20, "Too long")
.required("Required")
});
My SignUp component
<Formik
initialValues={{
name: '',
email: '',
password: ''
}}
validationSchema={userSignUpSchema}
onSubmit={createUser}
>
<Form className="form">
<label>Display Name</label>
<Field name="name" type="text" onChange={
(e) => {setUsernameReg(e.target.value);
}} />
<ErrorMessage
name="name"
component="div"
className="invalid-feedback"
/>
<label>Email Address</label>
<Field name="email" type="email" onChange={
(e) => {setEmailReg(e.target.value);
}} />
<ErrorMessage
name="email"
component="div"
className="invalid-feedback"
/>
<label>Password</label>
<Field name="password" type="password" onChange={
(e) => {setUsernameReg(e.target.value);
}}/>
<ErrorMessage
name="password"
component="div"
className="invalid-feedback"
/>
<button type="submit">Submit</button>
</Form>
</Formik>

Update specific value in Initial Values Formik

I am using Formik and Yup validation for my form which has firstname ,lastname & username. Username should be without spaces so I am using a onChange event and value. Yup validation is working for firstname and Lastname but not for username. When logging my values found out that username is not getting updated. I am a newbie please help me out. Thanks in advance.
const CustomTextInput =({label, ...props}) =>{
const [field, meta] = useField(props);
return(
<>
<label className="required" htmlFor={props.id || props.name}>{label}</label>
{meta.touched && meta.error ? (
<div className="error">{meta.error}</div>
):null}
<input className="text-input" {...field} {...props}/>
</>
)
}
function App(){
const [regnum, Setreg]= useState("");
function avoid(e){
Setreg(e.target.value.replace(/\s+/g,''));
}
return(
<Styles>
<Formik
initialValues={{
firstname:'',
lastname: '',
username:'',
phone1:'',
email:''
}}
validationSchema={
Yup.object({
firstname: Yup.string()
.required('Required'),
lastname: Yup.string()
.required('Required'),
username: Yup.string()
.min(4,"Username should be greater than 4 characters")
.max(15,"Wooah! Username cannot be that big")
.required('Required'),
})
}
onSubmit= {(values, { setSubmitting , resetForm }) => {
setTimeout(()=> {
//My api call here
resetForm()
setSubmitting(false);
},2000)
}
}>
{props => (
<Form>
<CustomTextInput label="First Name" name="firstname" type="text" placeholder="first Name"/>
<CustomTextInput label="Last Name" name="lastname" type="text" placeholder="Last Name"/>
<CustomTextInput label="UserName" name="username" type="text" value={regnum} onChange={(event)=>avoid(event)} placeholder="Spaces will be removed"/>
<div>
<button type="submit" >{props.isSubmitting ? "Loading..." : "Submit"}</button>
</div>
</Form>
</Formik>
</Styles>
);
}
export default App;
The question is, do you want to prevent the user from using a username with spaces? If so, the easiest way is to do it through yup.
validationSchema={
Yup.object({
firstname: Yup.string()
.required('Required'),
lastname: Yup.string()
.required('Required'),
username: Yup.string()
.required('Required').matches("/\s/", "Cannot contain spaces"),
})
}
Otherwise, if you allow the user to write the username with spaces, but you want it for whichever reasons without spaces, you have to manipulate the data in the submitHandler.
By giving it a custom onChange handler to username, you overrode formik since formik uses it's onChange under the hood. Manipulate the data in the submitHandler, prior to submitting, let formik do it's thing.

How to chain on custom validations using Yup

I want to add a .test() method to a validationSchema. The test is to look for common email domain misspellings. For instance, to check if gmail was misspelled "gmial" or "gnail".
const validationSchema = Yup.object({
email: Yup.string().lowercase()
.email('You have entered an invalid email')
.required('This field is required')
.test('test-name', 'Did you mean #gmail??????', function(value){
if (value.includes('gmial' || 'gnail'))
return this.createError('')
})
<Formik>
<Form>
<div className='form-control'>
<label htmlFor='email'>Email</label>
<Field type='text' id='email' name='email' />
<ErrorMessage name='email' component={TextError} />
</div>
</Form>
</Formik>
Return a boolean result in your test.
const validationSchema = Yup.object({
email: Yup.string()
.lowercase()
.email('You have entered an invalid email')
.required('This field is required')
.test('Gmail?', 'Did you mean #gmail?', (value) => value.includes('gmial' || 'gnail')),
});

Resources