score:0

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}\)?)?$/;

score:0

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);
};

score:1

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.

score:2

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>

Related Query

More Query from same tag