Upon entering my screen, there's no error displayed on the input field.
In my form, I take an input and run a graphql mutation on it. Once it's done, I reset the form. However, after resetting, I start seeing a Formik Error because the field is .required() and currently it's empty.
This error should only be shown when I am trying to submit the form without an input. It shouldn't show after I have submitted it once successfully.
const handleSubmitForm = (
values: FormValues,
helpers: FormikHelpers<FormValues>,
) => {
editLocationName({
variables: {
favouritePlaceId: route.params.id,
input: {customisedName: values.locationName}
},
});
helpers.resetForm();
helpers.setErrors({});
};
.....
<Formik
initialValues={initialValues}
onSubmit={handleSubmitForm}
validationSchema={validationSchema}>
{({ handleChange, handleBlur, handleSubmit, values }) => (
<View style={styles.searchFieldContainer}>
<View style={styles.form}>
<FieldInput
handleChange={handleChange}
handleBlur={handleBlur}
value={values.locationName}
fieldType="locationName"
placeholderText="Neuer Name"
/>
<ErrorMessage
name="locationName"
render={(msg) => <ErrorText errorMessage={msg} />}
/>
</View>
<View style={styles.buttonContainer}>
<ActionButton buttonText="Save" onPress={handleSubmit} />
</View>
</View>
)}
</Formik>
Validation Schema:
const favouriteLocationNameValidationSchema = yup.object().shape({
locationName: yup
.string()
.label('locationName')
.required('Required Field')
});
How can I reset the error message along with the form?
setErrors({}) did not work for me.
Adding it here, because suggestion requires code . Add following code and let me know if it helped.
helpers.resetForm({
errors: {},
touched: {}
});
Remove helpers.setErrors({});
Related
all,
I am using React-hook-form and yup as my validation tool in my react native project. But currently, I encountered an issue which is the handleSubmit function is not working in my ResetPasswordScreen component. And I cannot figure out why(in LoginScreen which has similar logic and it works fine). I appreciate it if anyone can help.
Here is my execution flow in ResetPasswordScreen, input email -> pass validation -> other inputs are displayed. And I am using isCodeSent param to control if other inputs are shown or not. Here is the PasswordResetScreen code
function PasswordResetScreen() {
const [isCodeSent, setIsCodeSent] = useState(false)
const {
control,
handleSubmit,
formState: { errors },
clearErrors,
reset,
} = useForm({
resolver: resetPwResolver,
})
const onSubmit = (data) => {
console.log('onSubmit ========> ', data)
if (isCodeSent) {
// api call to submit reset request...
} else {
// api call to sent code here...
//after sent code successfully, set isCodeSent param value as true to display the rest inputs
setIsCodeSent(true)
}
}
const dismissKeyboard = () => {
Keyboard.dismiss()
}
console.log('render foget pw page =====>', isCodeSent)
return (
<TouchableWithoutFeedback onPress={dismissKeyboard}>
<View style={styles.passwordResetContainer}>
<Text style={[globalStyles.label]}>
{isCodeSent ? '' : 'A passcode will be sent to your email'}
</Text>
<Controller
control={control}
render={({ field: { onChange, onBlur, value } }) => (
<TextInput
style={styles.input}
onBlur={onBlur}
onChangeText={onChange}
value={value}
placeholder='Email'
/>
)}
name='email'
/>
<Text style={styles.errorMsg}>
{errors.email ? errors.email.message : ''}
</Text>
{/* if code is sent, other inputs will be displayed */}
{isCodeSent ? (
<>
<Text>isCodeSent-----{isCodeSent}</Text>
<Controller
control={control}
render={({ field: { onChange, onBlur, value } }) => (
<TextInput
style={styles.input}
onBlur={onBlur}
onChangeText={onChange}
value={value}
placeholder='Passcode'
/>
)}
name='passcode'
/>
<Text style={styles.errorMsg}>
{errors.passcode ? errors.passcode.message : ''}
</Text>
<Controller
control={control}
render={({ field: { onChange, onBlur, value } }) => (
<TextInput
style={styles.input}
onBlur={onBlur}
onChangeText={onChange}
value={value}
placeholder='New Password'
/>
)}
name='newPassword'
/>
<Text style={styles.errorMsg}>
{errors.newPassword ? errors.newPassword.message : ''}
</Text>
<Controller
control={control}
render={({ field: { onChange, onBlur, value } }) => (
<TextInput
style={styles.input}
onBlur={onBlur}
onChangeText={onChange}
value={value}
placeholder='Confirm Password'
/>
)}
name='confirmPassword'
/>
<Text style={styles.errorMsg}>
{errors.confirmPassword ? errors.confirmPassword.message : ''}
</Text>
</>
) : null}
<Button
btnText={isCodeSent ? 'Submit' : 'Send'}
onPress={handleSubmit(onSubmit)}
/>
</View>
</TouchableWithoutFeedback>
)
}
export default PasswordResetScreen
here is the code of validation part :
// all the reg are defined before
const resetPwSchema = yup.object().shape({
email: yup
.string()
.matches(emailReg, 'Invalid email format')
.required(),
passcode: yup
.string()
.matches(passcodeReg, 'Invalid passcode format')
.required(),
newPassword: yup
.string()
.matches(passwordReg, 'Password must between 6 to 12 digits')
.required(),
confirmPassword: yup
.string()
.matches(passwordReg, 'Password must between 6 to 12 digits')
.required(),
})
export const resetPwResolver = yupResolver(resetPwSchema)
So, the code in onSubmit function 'console.log('onSubmit ========> ', data)' is not output at the console, which means the handleSubmit function is not working. Also this 'console.log('render foget pw page =====>', isCodeSent)' shows isCodeSend value is false all the time, which indicates the handleSubmit is not working.
I am thinking is it because I break the form into two parts but that does not make sense. I really cannot figure out what is the reason. If anyone can help, that would be so great.
I find the solution for that and figure out the reason for this behavior. The solution is: to define two schemas, one is for only the email field, another schema will include all the files (email, passcode, password, confirmPassword).
Here is my new code for schema:
const resetPwEmailSchema = yup.object().shape({
email: yup
.string()
.matches(emailReg, 'Invalid email format')
.required(requiredErrMsg('Email')),
})
export const resetPwEmailResolver = yupResolver(resetPwEmailSchema)
const resetPwSchema = yup.object().shape({
email: yup
.string()
.matches(emailReg, 'Invalid email format')
.required(requiredErrMsg('Email')),
passcode: yup
.string()
.matches(passcodeReg, 'Invalid passcode format')
.required(requiredErrMsg('Passcode')),
newPassword: yup
.string()
.matches(passwordReg, 'Password must between 6 to 12 digits')
.required(requiredErrMsg('Password')),
confirmPassword: yup
.string()
.matches(passwordReg, 'Password must between 6 to 12 digits')
.required(requiredErrMsg('Password')),
})
export const resetPwResolver = yupResolver(resetPwSchema)
The reason why this happen is when hit 'SEND' button, all the fields will be validated because the schema includes all fields(even other hidden fields), but except email field, other fields are empty(they are not shown), so the validation will fail, so the onSubmit function will never be run.
In react native I want to make a dynamic controller component. But i cant access errors with it. I using "react-hook-form" for form elements. So Its my component :
const {
control,
handleSubmit,
formState: {errors},
setValue,
} = useForm();
const DynamicController = ({req, pattern, name, label}) => {
return (
<>
<Text style={[t.textBase]}>{label}</Text>
<Controller
control={control}
defaultValue=""
rules={{
required: {
value: true,
message: 'Bu alan boş bırakılamaz!',
},
}}
render={({field: {onChange, onBlur, value}}) => (
<Input
errorText={errors[name].message}
error={errors[name]}
onBlur={onBlur}
placeholder={label}
onChangeText={onChange}
value={value}
/>
)}
name={name}
/>
</>
);
};
My Input Component is basicly simple input. My problem is when i give error name like that example i cant access errors.
Its how i use my component :
<DynamicController
label="Email"
name="Email"
pattern={true}
req={true}
/>
When i dont fill the element and log the submit its not showing any error. Its simple passing validate. So what can i do where do i make wrong ? thank you for answerings!!!
Is your Input a custom wrapper? If not, a better way do this using react-hook-form would be:
const {
control,
handleSubmit,
formState: {errors},
setValue,
} = useForm(
defaultValues: {
firstName: '', // form fields should be populated here so that the error can be displayed appropriately
lastName: ''
}
);
const DynamicController = ({req, pattern, name, label}) => {
return (
<>
<Text style={[t.textBase]}>{label}</Text>
<Controller
control={control}
defaultValue=""
rules={{
required: {
value: true,
message: 'Bu alan boş bırakılamaz!',
},
}}
render={({field: {onChange, onBlur, value}}) => (
<Input
onBlur={onBlur}
placeholder={label}
onChangeText={onChange}
value={value}
/>
)}
name={name}
/>
{errors[name] && <Text>This is required.</Text>}
</>
);
};
I have a Formik form where I take user input input & run a query after submitting.
When I click on the search button, a list is rendered. All list items have a button of their own. When I click on the green add button (from the list items) for the first time, the button does not work. Console log's content is not printed. Instead, the onBlur event of the inputField is triggered. However, if I click on the + button again, then it works and prints no. This problem is visible in simulators/phones, not on the web mode on the sandbox.
export const AddFriendScreen: React.FunctionComponent = () => {
const initialValues: FormValues = {
input: '',
};
const [showFlatList, setShowFlatList] = useState<UsersLazyQueryHookResult>(
'',
);
const handleSubmitForm = (
values: FormValues,
helpers: FormikHelpers<FormValues>,
) => {
loadUsers({
variables: {
where: {
OR: [
{ phoneNumber: newPhoneNumber },],
},
},
});
helpers.resetForm();
};
return (
<SafeAreaView style={styles.safeAreaViewContainer}>
<View style={styles.searchTopContainer}>
<View style={styles.formContainer}>
<Formik
initialValues={initialValues}
onSubmit={handleSubmitForm}
validationSchema={validationSchema}>
{({ handleChange, handleBlur, handleSubmit, values }) => (
<View style={styles.searchFieldContainer}>
<View style={styles.form}>
<FieldInput
handleChange={handleChange}
handleBlur={handleBlur}
value={values.input}
fieldType="input"
icon="user"
placeholderText="E-Mail oder Telefonnummer oder Name"
/>
<ErrorMessage
name="input"
render={(msg) => <ErrorText errorMessage={msg} />}
/>
</View>
<View style={styles.buttonContainer}>
<ActionButton buttonText="Suchen" onPress={handleSubmit} />
</View>
</View>
)}
</Formik>
</View>
<View style={styles.listHolder}>
{data && showFlatList !== null && (
<UsersFoundList
data={data}/>
)}
</View>
</View>
</SafeAreaView>
);
};
Snack Expo:
https://snack.expo.io/#nhammad/jealous-beef-jerky
[![enter image description here][1]][1]
Edit:
Found a workaround but still open to easier solutions. Still haven't figured out what's causing the issue exactly.
However, this causes the formik error to show up immediately after the form is submitted and data is returned/rendered. For instance, the error suggests that the input field should not be empty.
...
Why is the keyboard interfering in this?
Probably because when you call Keyboard.dismiss() you are actually unfocusing the input, which will trigger the validateOnBlur event.
So when you unfocus a field (blur), it will try to validate (validateOnBlur), and because the field is empty, it will show the validation.
The error should only show up when we are trying to resubmit the form without an input.
If the error should only show when you submit the form, you should pass to the Formik component validateOnBlur={false}. With this, it won't show the error message when you call Keyboard.dismiss(), because it will remove the validation on blur.
But it still validates when the input changes, triggering validateOnChange. You can also pass to the Formik component validateOnChange={false} to disable the validation on change and it will only validate when you submit the form (press the button).
Please notice that validateOnChange and validateOnBlur are true by default.
Edit:
You need to add validateOnBlur={false} but instead of calling Keyboard.dismiss() you need to call fieldRef.current.blur(). But for that to work, you need to use React.forwardRef in your input component.
So you need to create the fieldRef
const fieldRef = useRef();
Pass to the component
<FieldInput
ref={fieldRef} // passing the ref
handleChange={handleChange}
handleBlur={handleBlur}
value={values.input}
fieldType="input"
icon="user"
placeholderText="input"
/>
And you need to wrap around your FieldInput with React.forwardRef and pass the ref to the Input component.
// wrapping the component with React.forwardRef
export const FieldInput: React.FunctionComponent<FieldInputProps> = React.forwardRef(({
handleChange,
handleBlur,
fieldType,
placeholderText,
value,
style,
rounded,
//ref,
}, ref) => {
return (
<Item rounded={rounded} style={[styles.inputItem, style]}>
<Input
ref={ref} // passing the ref
autoFocus={true}
autoCapitalize="none"
style={styles.inputField}
placeholder={placeholderText}
keyboardType="default"
onChangeText={handleChange(fieldType)}
onBlur={handleChange(fieldType)}
value={value}
/>
</Item>
);
});
And now in the handleSubmitForm you call fieldRef.current.blur()
const handleSubmitForm = (
values: FormValues,
helpers: FormikHelpers<FormValues>,
) => {
// other logic from your submit
if (fieldRef && fieldRef.current)
fieldRef.current.blur();
};
With this, you will solve the problems of your question and from the comments.
Working example
Explanation about useRef
We need to use the useRef hook so we can get the element of the Input component to be able to call the blur function so don't be focused in the input and can click in the plus button.
We need to use the useRef hook because that is how you create a ref in functional components.
I want to trigger formik errors and touched whenever the input is clicked and the value is not correct .
I pass formik props to the input component like this :
const initialValues = {
title: ''
};
const validationSchema = yup.object({
title: yup.string().max(50, 'less than 50 words !!').required('required !!')
});
function Add() {
<Formik initialValues={initialValues} onSubmit={onSubmit} validationSchema={validationSchema}>
{(props) => {
return (
<Form>
<AddTitle props={props} />
</Form>
);
}}
</Formik>
);
}
Here I'm trying to display error message whenever the input is touched and there is an error with it like this :
import { Input } from 'antd';
function AddTitle(props) {
console.log(props.props);
return (
<Field name="title">
{() => {
return (
<Input
onChange={(e) => {
props.props.setFieldValue('title', e.target.value)
}}
/>
);
}}
</Field>
<ErrorMessage name="title" />
<P>
{props.props.touched.title && props.props.errors.title && props.props.errors.title}
</P>
</React.Fragment>
);
}
But ErrorMessage and the paragraph below it doesn't work when the input is touched and empty .
In console log it shows that input doesn't handle formik touched method and it only triggers the error for it :
touched:
__proto__: Object
errors:
title: "less than 50 words !"
__proto__: Object
How can I use ErrorMessage properly while passing in formik props to a component and using a third library for inputs ?
Fixed the issue by adding the onBlur to the input and ErrorMessage is working fine :
<Field name="title">
{() => {
return (
<Input
onBlur={() => props.props.setFieldTouched('title')}
onChange={(e) => {
props.props.setFieldValue('title', e.target.value);
}}
/>
);
}}
</Field>
<P class="mt-2 text-danger">
<ErrorMessage name="title" />
</P>
I have a TextInput and when I autofill my email address I have a space after my email.
I try this
const email = values.email.trim();
and also
const email = values.email.replace(/\s+/g, ' ');
but it's doesn't work. Someone know how to remove the space after the autofill?
return (
<Formik
enableReinitialize={true}
initialValues={{ email: this.props.navigation.getParam("email") }}
validationSchema={yup.object().shape({
email: yup
.string()
})}
onSubmit={async (values) => {
const email = values.email;
}
}
>
{({ handleChange, handleSubmit, values, errors, touched, setFieldTouched }) => (
<View>
{
<View>
<TextInput
value={values.email}
placeholder="Email"
autoCapitalize="none"
autoCorrect={false}
onBlur={() => setFieldTouched("email")}
onChangeText={handleChange("email")}
autoCompleteType={"email"}
/>
<View>
<TouchableOpacity
onPress={handleSubmit}
>
<Text style={styles.textButton}>Valider</Text>
</TouchableOpacity>
</View>
</View>
)}
</Formik>
);
If you haven't found a solution for this situation yet, here's one way that solves it:
onChangeText={(val) => setFieldValue('login', val.trim())}
Instead of using handleChanges, you can use setFieldValue, then "trim" val.
<Text
...
value={this.state.emailID}
/>
Reuse your trim logic and assign the new value to the state variable(emailID) in "onChangeText" of the textinput