Multiple schemas when using Yup and React form hook? - reactjs

I have a component with 2 forms. 1 for sign in, and 1 for sign up.
const schema = yup.object().shape({
email: yup.string().email(),
password: yup.string().min(4).max(20).required(),
});
export const LoginForm = ({onRequestClose} : Props) => {
const {control, handleSubmit, formState: {errors}} = useForm<IFormInputs>({
resolver: yupResolver(schema),
});
I have a Yup schema which I use in the react form hook resolver. This works for my login form because that only has a email and password
But on the sign up form I also have userName and repeatPassword. The issue is that if I add those properties to the schema the login doesn't work anymore. I think because it tries to validate fields the form doesn't supply but I'm not sure.
How do you add multiple schema's for forms in Yup?

Resolved it by doing this:
const {control: controlLogin, handleSubmit: handleSubmitLogin, formState: {errors: errorsLogin}} = useForm({
resolver: yupResolver(loginSchema),
});
const {control: controlSignUp, handleSubmit: handleSubmitSignUp, formState: {errors: errorsSignUp}} = useForm({
resolver: yupResolver(schemaSignUp),
});

Yes, it's working, I'm used in my project like:
//Yup schema for validation
const editvalidationSchema = Yup.object().shape({
remark: Yup.string().required("Required"),
});
const {handleSubmit: handleSubmitEditRemark, register: reg, formState: {errors: errorsEditRemark}, setValue, clearErrors} = useForm({
resolver: yupResolver(editvalidationSchema),
});
// use
<Form onSubmit={handleSubmitEditRemark(editRemark)}>
<input name="remark" type="text" className={`form-control ${errorsEditRemark.remark ? "is-invalid" : ""}`} defaultValue={data.remark)} autoFocus {...reg("remark")} />
//submit button
<Button type="submit"> Submit </Button>
</Form>

Related

react-hook-form + yup validation field value

I`ve got this yup schema:
`const ModalEditCard = () => {
const validationSchema = Yup.object().shape({
title: Yup.string()
.required("Поле обязательно для заполнения")
.max(50, "Заголовок не более 50 символов")
});`
React-hook-form options:
const {
register,
handleSubmit,
formState: { errors, isValid }
} = useForm<UserSubmitForm>({
resolver: yupResolver(validationSchema),
mode: "onSubmit",
reValidateMode: "onChange",
});
and this textarea:
` <textarea
{...register(name)}
rows={1}
className="modal__textArea"
placeholder={placeholder}
/>`
I whant to show how mach symbols user has to write. Example I have max 50 symbols for textarea. If I write hello, under field i want to see "45". And it`s should be just message. Not validate
I had custom hook for count symbols. But i think, Yup could help me

Validation does not work in react-hook-form with dynamic name fields

I use react-hook-form and yup-validation. An array with custom names comes to me from the backend. Validation doesn't work for me. What am I doing
export const createValidationSchema = (arr) => {
const validationObject = {};
arr?.forEach(({name}) => {
validationObject[name] = string().required("Required field")
});
return object().shape({
editFields: array()
.of(object().shape(validationObject))
.required("Required field")
});
};
const { control, register, handleSubmit, formState: { errors, isValid } } = useForm<Form>({
resolver: yupResolver(createValidationSchema(documentDetail?.template?.items)),
mode: "onBlur"
});
const { fields, append } = useFieldArray<Form>({
control,
name: "editFields",
});
below in JSX:
{fields?.map(({ name, title, type, id }) => (
{type === "STRING" && (
<InputField
validationType="underlineText"
{...register(name)}
required
/>
)}
)
but errors always empty. What am I doing wrong?
My mistake. In validationObject I have an object, and in the schema I assigned it as an array

How to use Formik with React-Select with isMulti?

I'm building a system where we can create a project and assign it to several users by filling up a form using Formik, Yup and React-Select.
However, I'm really struggling with passing the array of users when submitting my form using Formik, Formik doesn't receive the data from React-Select.
Here are snippets of the code:
const validationSchema = () => {
return Yup.object().shape({
title: Yup.string().required('A title is required'),
description: Yup.string().required('A description is required'),
assigned: Yup.array().required('At least one assigned user is required'),
});
};
const formik = useFormik({
initialValues: {
title: '',
description: '',
assigned: [],
},
validationSchema,
validateOnBlur: false,
validateOnChange: false,
onSubmit: (data) => {
ProjectService.create(data).then(() => {
navigate('/projects');
window.location.reload();
});
},
});
The component containing the options to select:
Assign to:
<SelectList
name="users"
options={convertToReactSelectObject(users)}
onChange={(assignedUsers) =>
formik.setFieldValue('assigned', assignedUsers.value)
}
value={formik.values.assigned}
/>
{formik.errors.assigned ? formik.errors.assigned : null}
Does someone have an idea of what I should fix to make it work?
Thank you!
My mistake was that I was trying to only pass the "value" field of the array to Formik which doesn't work. So I've passed the whole array of assigned users
<SelectList
options={convertToReactSelectObject(users)}
value={formik.values.assigned}
onChange={(assignedUser) =>
formik.setFieldValue('assigned', assignedUser)
}
/>
Then on the backend, the new project will only take the value field of the assigned users.
// Create a Project
const project = new Project({
title: req.body.title,
description: req.body.description,
assigned: req.body.assigned.map((e) => e.value),
});

formik onChange working not asynchronously

name: Yup.string()
.required(),
});
const {
errors,
touched,
getFieldProps,
handleChange,
} = formik;
function onChange(e) {
if (Object.keys(errors).length === 0) {
**(HERE I HAVE errors.length 0, despite this my input is empty and is required, so I shuld have errors here. I have error here, but with delay, )**
dispatch(setInvitationConfigParam({ name: e.target.name, value: e.target.value }));
}
handleChange(e);
}
<InputWrapper>
<TextInput
customClass="input"
error={false}
textFieldProps={{
label: t('configurationForm.settings.nameConfiguration'),
error: hasErrors('name', errors, touched),
helperText: getErrors('name', errors, touched),
...getFieldProps('name'),
onChange,
}}
tooltipTitle={t('configurationForm.settings.nameConfigurationDescription')}
/>
What I want and why I have problem.
I want to sending dispach function every time when user change his input name, but if the input name is empty I didn't want sendig dispatch.
The problem is that: object errors show errors with a delay.
how can I have errors with no dealy?

Yup validation for loginID with dynamic data type(either email or phone number)

I am trying to implement login functionality using Formik and Yup. This is my current schema
let loginSchema = yup.object().shape({loginId:
yup
.string()
.email("That doesn't look like a valid email")
.required("This field is required."),password: yup.string().required("This field is required."),});
But my loginId can either be a phone number or email. So, how do i add the validation based on the type of field that has been entered in the form. If its an email, trigger validation for email or if its phone number, i want to validate it against a regex.
Input field that accepts both email and phone:
const LoginSchema = yup.object().shape({
email_or_phone: yup.string()
.required('Email / Phone is required')
.test('email_or_phone', 'Email / Phone is invalid', (value) => {
return validateEmail(value) || validatePhone(parseInt(value ?? '0'));
}),
password: yup.string().required()
});
const validateEmail = (email: string | undefined) => {
return yup.string().email().isValidSync(email)
};
const validatePhone = (phone: number | undefined) => {
return yup.number().integer().positive().test(
(phone) => {
return (phone && phone.toString().length >= 8 && phone.toString().length <= 14) ? true : false;
}
).isValidSync(phone);
};
Solved email and PhoneNumber Validation
const loginValidationSchema = Yup.object({
email: Yup.string().when("isEmail", {
is: '1',
then: Yup.string()
.email("Please enter valid email")
.required("email cannot be empty"),
otherwise: Yup.string()
.required("phonenumber cannot be empty")
.min(6, 'phonenumber must be at least 6 char'),
}),
password: Yup.string()
.required("Password cannot be empty")
.min(6, 'Password must be at least 6 char'),
});
<Formik
validationSchema={loginValidationSchema}
initialValues={{ isEmail: 0, email: '', password: '' }}
onSubmit={}
>
{({ handleChange, handleBlur, handleSubmit, values, errors,
touched }) => (
<View>
<TextInput
placeholder="ramchandran2897#gmail.com"
onChangeText={(event)=>{
handleChange("email")(event)
if(Number(values.phonenumberOrEmail)){
handleChange("isEmail")('0')
}else{
handleChange("isEmail")('1')
}
}}
onBlur={handleBlur('email')}
value={values.email}
keyBoardType="email-address"
autoFocus={false}
returnKeyType='next'
returnKeyLabel='>'
onSubmitEditing={() => passwordRef.current?.focus()}
error={errors.email}
touched={touched.email}
/>
<TextInput
placeholder="****"
placeholderTextColor={Colors.darkLight}
onChangeText={handleChange('password')}
onBlur={handleBlur('password')}
value={values.password}
secureTextEntry={hidePassword}
isPassword={true}
hidePassword={hidePassword}
setHidePassword={setHidePassword}
min={6}
autoCapitalize='none'
keyboardAppearance='dark'
onSubmitEditing={handleSubmit}
ref={passwordRef}
error={errors.password}
touched={touched.password}
/>
<TextInput
onChangeText={handleChange('isEmail')}
keyBoardType='none'
/>
</View>
)}
</Formik>
If you want to dynamically validate your fields, use when for the validationSchema.
An example from the documentation.
let schema = object({
isBig: boolean(),
count: number()
.when('isBig', {
is: true, // alternatively: (val) => val == true
then: yup.number().min(5),
otherwise: yup.number().min(0),
})
.when('$other', (other, schema) => (other === 4 ? schema.max(6) : schema)),
});
So in your case you have to craft your is statement to decide whether it is an email of a phone number, then you can attach validation accordingly.
I didn't work with formik but face the same situation as you face here. I was working with Form Hook. Maybe somehow it will help you or someone.
First of all, add a hidden input after your main input in which the user will add phone or email. On change of that main input, you have to check that if this string contains '#' or not. If it contains # then make a state and update state with true like this.
const onChangeEmailOrPhone = (event) => {
let email = event.target.value.includes('#');
if (email) {
setIsEmail(true);
} else {
setIsEmail(false);
}
};
and set this state value to the hidden input just like this
<TextField
type="hidden"
name="isEmailValue"
inputRef={register}
value={isEmail}
/>
Following should be your validation schema to check weather isEmailValue is true or not, If true then you have to validate for email otherwise for the phone just like this.
let loginSchema = yup.object().shape({
emailOrPhone: yup.string().when('isEmailValue', {
is: 'true',
then: yup
.string()
.email('Please enter valid email')
.required('This field is required'),
otherwise: yup
.string()
.matches(phoneRegex, 'Please enter valid phone number')
.required('This field is required'),
})});
Following is phoneRegex for your help
const phoneRegex = /^(\+?\d{0,4})?\s?-?\s?(\(?\d{3}\)?)\s?-?\s?(\(?\d{3}\)?)\s?-?\s?(\(?\d{4}\)?)?$/;

Resources