I use react-hook-form with yup to validate my forms.
I want to know all required fields of a schema to display some information in form (like '*' for required fields). We could achieve this with this line of code :
schema.describe().fields[field].tests.findIndex(({ name }) => name === 'required'
However, this code doesn't work for conditional validation.
Schema example :
const schema = yup.object().shape({
email: yup
.string()
.email()
.required(),
isProfileRequired: yup
.boolean(),
profile: yup
.object()
.when('isProfileRequired',{
is: (isProfileRequired) => isProfileRequired,
then:
yup
.object()
.nullable()
.required()
})
})
Is there a way to retrieve this informations within the form ?
I use react-hook-form with yup to validate my forms.
I want to know all required fields of a schema to display some information in form (like '*' for required fields). We could achieve this with this line of code :
schema.describe().fields[field].tests.findIndex(({ name }) => name === 'required'
However, this code doesn't work for conditional validation.
Schema example :
const schema = yup.object().shape({
email: yup
.string()
.email()
.required(),
isProfileRequired: yup
.boolean(),
profile: yup
.object()
.when('isProfileRequired',{
is: (isProfileRequired) => isProfileRequired,
then:
yup
.object()
.nullable()
.required()
})
})
Is there a way to retrieve this informations within the form ?
Share Improve this question asked Oct 20, 2020 at 7:26 Sponky30Sponky30 631 silver badge4 bronze badges 1- 1 Also discussed in this issue – colinD Commented Mar 4, 2022 at 10:46
5 Answers
Reset to default 7There is actually no "nice" way to do it but this works:
function isRequired(field){
return schema.fields[field]._exclusive.required || false
}
Notice: schema.fields[field]._exclusive.required
returns true if required or undefined.
Testing exclusiveTests instead of _exclusive worked for me.
const isRequired =
validationSchema?.fields[aField.name]?.exclusiveTests?.required || false;
Based on this comment in the link that @colinD shared, this is working well for me.
const isFieldRequired = (validationSchema, id) =>
validationSchema
.describe()
.fields[id].tests.some(({ name }) => name === 'required')
This checks if any test
in a particular field has a name
equal to 'required'
. For example:
const validationSchema: AnyObject = object({
username: string().required(),
email: string().email().required(),
})
will produce a schema with fields
that look like:
{
username: {
...
tests: [
{name: 'required', ...}, // is required
]
},
email: {
...
tests: [
{name: 'email', ...},
{name: 'required', ...}, // is required
]
},
}
In yup 1.2.0
, it seems the property to use is now the optional
boolean on SchemaDescription
.
This is a function that returns a Record<keyof T, bool>
where T
is the generic parameter of the schema, and the boolean is true
if the field is marked as required, false
otherwise.
import { ObjectSchema, SchemaDescription } from "yup";
type FormObject = Record<string, unknown>;
const getRequiredFields = <T extends FormObject>(
schema: ObjectSchema<FormObject>
) => {
const fields = schema.describe().fields as Record<keyof T, SchemaDescription>;
return Object.entries(fields).reduce((newObj, [key, schemaDescription]) => {
newObj[key as keyof T] = !schemaDescription.optional;
return newObj;
}, {} as Record<keyof T, boolean>);
};
Usage:
const schema = yup.object({
name: yup.string().required(),
otherField: yup.string().optional(),
code: yup.number().required(),
});
const fields = getRequiredFields(schema);
// fields = {
// name: true,
// otherField: false,
// code: true,
// }
I made a hook based on @colinD solution.
export const useIsFieldRequired = <T extends FormObject>(
schema: ObjectSchema<T>,
) => {
const getRequiredFields = () => {
const fields = schema.describe().fields as Record<
keyof T,
SchemaDescription
>;
return Object.entries(fields).reduce(
(newObj, [key, schemaDescription]) => {
newObj[key as keyof T] = !schemaDescription.optional;
return newObj;
},
{} as Record<keyof T, boolean>,
);
};
const requiredFields = getRequiredFields();
const isFieldRequired = (field: keyof T): boolean => {
return requiredFields[field];
};
return {
isFieldRequired,
};
};
Usage:
const { isFieldRequired } = useIsFieldRequired<FormValues>(schema);
return (
<input name="input-name" required={isFieldRequired('input-name'} />
)