-
-
Save manzoorwanijk/5993a520f2ac7890c3b46f70f6818e0a to your computer and use it in GitHub Desktop.
import * as yup from 'yup'; | |
import { setIn } from 'final-form'; | |
const validationSchema = yup.object({ | |
email: yup.string().email(), | |
shipping: yup.object({ | |
name: yup.string(), | |
phone: yup.object({ | |
code: yup.string().matches(/^\+\d+$/i), | |
number: yup.number().max(10), | |
}), | |
address: yup.string(), | |
zip: yup.string(), | |
}), | |
billing: yup.object({ | |
name: yup.string(), | |
address: yup.string(), | |
zip: yup.string(), | |
}), | |
items: yup.array().of( | |
yup.object({ | |
id: yup.number(), | |
price: yup.number(), | |
quantity: yup.number(), | |
}) | |
), | |
}); | |
// To be passed to React Final Form | |
const validateFormValues = (schema) => async (values) => { | |
if (typeof schema === 'function') { | |
schema = schema(); | |
} | |
try { | |
await schema.validate(values, { abortEarly: false }); | |
} catch (err) { | |
const errors = err.inner.reduce((formError, innerError) => { | |
return setIn(formError, innerError.path, innerError.message); | |
}, {}); | |
return errors; | |
} | |
}; | |
const validate = validateFormValues(validationSchema); | |
const MyForm = () => ( | |
<Form // from react-final-form | |
onSubmit={onSubmit} | |
validate={validate} | |
/> | |
); |
Nicely done! It should be converted to yup wrapper for react :)
@nfantone, thank you.
But in my opinion, a hook is not needed for this and it can be a simple function which can be called outside the component because the schema may not change per component.
@manzoorwanijk The module I shared exports both a "simple function" and a hook to use inside components. You can chose to use the one that suits your needs. The hook is just a trivial wrapper that memoizes the function - so both approaches work pretty much the same for the basic scenario. If your components are uncomplicated enough, you're right: you could create the validate
function outside the component closure once and re-use it.
That's not always possible, however. A hook may be needed if:
- you need to support i18n on message errors.
- you need/want to defer the creation of your
yup
schema to component mount. - your
yup
schema depends on some data from your component state and needs to be (re-)generated off of it (e.g.: wizards, dynamic forms).
with typescript resolver
import { setIn } from "final-form";
import { AnySchema, ValidationError } from "yup";
export function yupResolver(schema: AnySchema) {
return async (value: object) => {
try {
await schema.validate(value, { abortEarly: false });
} catch (error) {
if (error instanceof ValidationError) {
return error.inner.reduce((errors, error: ValidationError) => {
const path = error.path ?? "global";
return setIn(errors, path, error.message);
}, {});
}
throw error;
}
};
}
<Form
onSubmit={(fields) => console.log(fields)}
validate={yupResolver(SupplierSchema)}...
with typescript resolver
import { setIn } from "final-form"; import { AnySchema, ValidationError } from "yup"; export function yupResolver(schema: AnySchema) { return async (value: object) => { try { await schema.validate(value, { abortEarly: false }); } catch (error) { if (error instanceof ValidationError) { return error.inner.reduce((errors, error: ValidationError) => { const path = error.path ?? "global"; return setIn(errors, path, error.message); }, {}); } throw error; } }; }<Form onSubmit={(fields) => console.log(fields)} validate={yupResolver(SupplierSchema)}...
thank you
I created a hook based off of this gist.
Feel free to comment or borrow ideas.