Skip to content

Instantly share code, notes, and snippets.

@manzoorwanijk
Last active August 22, 2024 11:44
Show Gist options
  • Save manzoorwanijk/5993a520f2ac7890c3b46f70f6818e0a to your computer and use it in GitHub Desktop.
Save manzoorwanijk/5993a520f2ac7890c3b46f70f6818e0a to your computer and use it in GitHub Desktop.
How to properly use yup validation schema with (React) Final Form?
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}
/>
);
@manzoorwanijk
Copy link
Author

I have updated the gist.

@nfantone
Copy link

nfantone commented Aug 27, 2020

I created a hook based off of this gist.

const validationSchema = yup.object().shape({
  email: yup.string().email().required(),
  password: yup.string().min(5).required()
});

const initialValues = Object.freeze({
  email: '',
  password: ''
});

function Login() {
  const validate = useValidationSchema(validationSchema); // <-- Create RFF validation function

  const handleSubmit = useCallback(
    ({ email, password }) => alert(`Signing in with ${email} // ${password}`),
    []
  );

  return (
    <Form validate={validate} initialValues={initialValues} onSubmit={handleSubmit}>
      {/* ... */}
    </Form>
  );
}

export default Login;

Feel free to comment or borrow ideas.

@miooim
Copy link

miooim commented Aug 27, 2020

Nicely done! It should be converted to yup wrapper for react :)

@manzoorwanijk
Copy link
Author

@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.

@nfantone
Copy link

nfantone commented Aug 28, 2020

@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).

@franklinST-05
Copy link

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

@shifoc
Copy link

shifoc commented Aug 22, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment