-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Field level validation dependent on other fields. #1737
Comments
Having same issue here. We have range inputs, where numbers should not revert range (e.g. min value should not be more than max and vice versa). Lacking this feature forces us to do some complicated solutions like top level validation which is really hard to follow with a huge form. I believe you pass the whole form object as a second parameter into field validation function then I can decide whatever I want to use it for. |
I agree. If the formik bag is available to a |
This missing feature is causing a lot of issues for me as well, especially with related fields like password and re-enter password which need cross-validation. I would prefer to use field-level validation for this but it's impossible unless you can get access to up-to-date values. I tried to pass the current form object into my validation function but the values are one render cycle behind and therefore useless. Any update on making this possible? |
We are also finding it troublesome for implementing cross field validation. It would be great, when using the useField-Hook, FieldMetaProps would not only include the value "plucked out from values", but also the values themselves. Like that, cross-field validation would be easy to implement. |
I would also like support for something like this. Form level validation would work if I was able to tell if fields had been touched or not. |
Seems reasonable, can this be done with useEffect though? |
I am not sure why/where you would use useEffect. I thought it should be easy to not "pluck out from values" the one value of the respective field, but instead give all those values to the field, isn't it? |
Yeah that’s fine by me |
Great! Are you saying that you will be working on it? |
I’m running into the same issue with range fields. In my case, validation props like min/max are strings that refer to other values in the formik values object. So my range inputs are just components that wrap formik inputs. I managed to create a workaround by using validation callback and using useFormikContext’s setFormikState to register the validation for each field on mounting. When the validation callback is called I iterate through each yup validator and provide it the values object. I tried to use the useField validate prop, but there wasn’t any point because I had to pass in the formik values object. I got that from useFormikContext. This resulted in duplicated rendering and terrible performance. |
I have this same issue. In my form I have a date field, and two other time fields in which their time must be after 'now', if the selected day is today. So I'm having trouble validating these time fields dynamically when the date is changed. I tried something like @Menardi mentioned above, using the |
I successfully managed to get my validation to work with a little hack: const { validateField, values } = useFormikContext();
const fieldValue1 = values?.fieldValue1;
const fieldValue2 = values?.fieldValue2;
const validate = useCallback(
value => {
// validation logic
},
[fieldValue1, fieldValue2]
);
...
const [field, meta, helpers] = useField({
name: fieldName,
validate
});
useEffect(() => {
const timeoutId = setTimeout(() => validateField(fieldName), 50);
return () => clearTimeout(timeoutId);
}, [fieldName, validate, validateField]); By the time the Is it a hack? Yes. Does it work? Yes. |
I managed to reference other fields with custom validation using a ref. Full example below -- don't let the length of the example throw you off, it's a clean and simple solution. Relevant code: TLDR: Any extra field I want to reference in my validation is wrapped within import React, { useRef } from 'react';
import { Formik, Form, Field } from 'formik';
import MyFeedbackComponent from '/path/to/MyFeedbackComponent';
import * as yup from 'yup';
const MyComponent = props => {
const fieldRef = useRef();
const defaults = {
myReferenceField: null,
myOtherField: null,
};
const schema = yup.object().shape({
myReferenceField: yup.number().required('Reference field is required.'),
myOtherField: yup.number().required('My other field is required.'),
});
const handleSubmit = (values, resetForm, setSubmitting) => {
const { myReferenceField, myOtherField } = values;
// Your submit payload, API call, form/submit reset etc. here...
};
const myValidationFn = otherFieldVal => {
const limit = 100;
const referenceVal = fieldRef.current.value;
const errorMsg = `Total must be under ${limit}.`;
if (referenceVal) {
// Your custom condition and error.
const underLimit = otherFieldVal * referenceVal < limit;
return !underLimit && errorMsg;
}
return true;
};
return (
<Formik
initialValues={defaults}
enableReinitialize={true}
validationSchema={schema}
onSubmit={(values, {resetForm, setSubmitting}) => (
handleSubmit(values, resetForm, setSubmitting)
)}>
{form => {
return (
<Form>
<label>
/* This field is written this way so we can attach a ref to it.
* That way the most recent state value for this field is
* available in our custom validation function. */
<Field type="number" name="myReferenceField">
{({form, field}) => <input ref={fieldRef} {...field} />}
</Field>
<MyFeedbackComponent name="myReferenceField" />
</label>
<label>
// This field will call a custom validation function that will
// reference fieldRef's value.
<Field
type="number"
name="myOtherField"
validate={otherFieldVal => myValidationFn(otherFieldVal} />
<MyFeedbackComponent name="myOtherField" />
</label>
<button type="submit" disabled={form.isSubmitting}>
Submit
</button>
</Form>
)};
}
</Formik>
);
}; |
Any news on this? |
I agree that the values should be passed as a second argument to the validate function. Here's a quick workaround in the meantime: const {
values,
validateField
}: FormikProps<YourFormData> = useFormikContext();
useEffect(() => {
validateField("amount");
}, [values.amountType, validateField]); |
It also works to provide the values to your validation function at the time of render, i.e have your validation function derived from a returned function: export const validateLaunchDateIsBeforePreLaunch = ( values ) => {
return (launchDate) => {
const preLaunchDate = values.preLaunch;
const myIsBefore = (preLaunchDate && isBefore(new Date(launchDate), new Date(preLaunchDate)));
if(myIsBefore) {
return "Launch date is before pre launch date";
}
};
};
const { values } = useFormikContext();
return (
<Field
name="launchDate"
validate={validateLaunchDateisBeforePreLaunch(values)}
.../>
)} |
Not sure how to achieve this if you're using a class component instead of functional. |
@boroth you can use class MyClassComponent {
componentDidUpdate(props) {
console.log('validate your thing here');
}
}
const MyConnectedClassComponent = connect(MyClassComponent);
const MyForm = () => {
return <Formik {...formikProps}>
<MyConnectedClassComponent />
</Formik>;
}; My plan, if TypeScript enables it, is eventually to support: const MyDependentField = () => <Field
name="myField"
include={state => ({ otherFieldValue: state.values.otherField })}
validate={(value, { otherFieldValue }) => value === otherFieldValue ? "Fields cannot be the same" : ""}
/> However, that will be dependent on #1334 and #3089 because we need to resolve types before adding this functionality, as well as optimize subscriptions to Formik's API so this isn't super expensive. |
@johnrom, I could kiss you right now. Been jumping through hoops trying to get this to work and I had totally missed the |
Formik's |
To fix it here, first I created a useState, which will store the variable that causes dependency. In my case, the card's flag is required to validate the card number.
Then, I transformed the onChange function into an asynchronous function. Thus, when the card brand changes, we execute the validateField of the card number in parallel. That way, it doesn't need a timeout or anything.
|
Recently I have moved to https://github.com/final-form/react-final-form because they support validate function with following signature: ( value: FieldValue, allValues: object, meta?: FieldState<FieldValue>) => any
It would be great if @johnrom 's proposal would be accepted ( also in |
This seemed to work for me using <Formik
initialValues={{ newPassword: '', confirmNewPassword: '' }}
validationSchema={Yup.object().shape({
newPassword: Yup.string()
.required('New Password is required.')
.min(8, 'New Password must be at least 8 characters long.')
.matches(/\d/, 'New Password must contain at least 1 number.')
.matches(
/[A-Z]/,
'New Password must contain at least 1 uppercase letter.',
),
confirmNewPassword: Yup.string().oneOf(
[Yup.ref('newPassword')],
'Passwords must match',
),
})} |
Any updates on this requested logic. On field level validation, having the ability to validate a field's value based on another field's value using the most up to date values object after the other field's value has been changed would be super helpful. |
🚀 Feature request
Current Behavior
<Field validate={(fieldValue) => ... } />
For now field level validaiton funciton gives us only field value.
Desired Behavior
<Field validate={(fieldValue, formikBag) => ... } />
I want to have access to formikBag inside of field level validation.
Suggested Solution
Pass additional argument with formikBag to validate callback function.
Who does this impact? Who is this for?
I want to have possibility to access other formik values to create dependent validation. E.g. I have Field1 and Field2. Field2 should be invalid if Field1 is true and Field2 have some values inside. Form is reused and you can compound it from smallest reuseable pieces. It means sometimes you can have Field2 defined or undefined, that is why I don't want to use for this case global <Formik validation method.
Describe alternatives you've considered
Global Formik validation method - no, bcs of -> I want to see validation on component Blur, and want to compound form from smallest Fields element that can be included or not dynamically.
Field Field nesting just to have form values inside of second Field validation
No. Just have a look at this. Unnecessary component just to retrieve values I should have access to.
The text was updated successfully, but these errors were encountered: