Skip to content

Instantly share code, notes, and snippets.

@nfantone
Last active May 29, 2024 01:55
Show Gist options
  • Save nfantone/9ab600760db8774ab4873cb1a3a22f26 to your computer and use it in GitHub Desktop.
Save nfantone/9ab600760db8774ab4873cb1a3a22f26 to your computer and use it in GitHub Desktop.
Use `yup` with `react-final-form`
import { setIn } from 'final-form';
import { useMemo } from 'react';
/**
* Sets the `innerError.message` in an `errors` object at the key
* defined by `innerError.path`.
* @param {Object} errors The object to set the error in.
* @param {{ path: string, message: string }} innerError A `yup` field error.
* @returns {Object} The result of setting the new error message onto `errors`.
*/
const setInError = (errors, innerError) => {
return setIn(errors, innerError.path, innerError.message);
};
/**
* Empty object map with no prototype. Used as default
* value for reducing the `err.inner` array of errors
* from a `yup~ValidationError`.
* @type {Object}
*/
const emptyObj = Object.create(null);
/**
* Takes a `yup` validation schema and returns a function that expects
* a map of values to validate. If the validation passes, the function resolves to `undefined`
* (signalling that the values are valid). If the validation doesn't pass, it resolves
* to a map of invalid field names to errors.
* @param {import('yup').ObjectSchema} schema `yup` schema definition.
* @returns {(values: Object) => Promise<?Object>} An async function that expects some `values`
* and resolves to either `undefined` or a map of field names to error messages.
*/
export const makeValidate = schema => {
return async function validate(values) {
try {
await schema.validate(values, { abortEarly: false });
} catch (err) {
return err.inner.reduce(setInError, emptyObj);
}
};
};
function useValidationSchema(schema) {
const validate = useMemo(() => makeValidate(schema), [schema]);
return validate;
}
export default useValidationSchema;
@litewarp
Copy link

litewarp commented Feb 12, 2022

Converted it to TS for anyone stumbling upon this:

// nfantone/use-validation-schema.js
// https://gist.github.com/nfantone/9ab600760db8774ab4873cb1a3a22f26
import { setIn } from 'final-form'
import { useMemo } from 'react'
import { AnyObjectSchema, ValidationError } from 'yup'

/**
 * Sets the `innerError.message` in an `errors` object at the key
 * defined by `innerError.path`.
 */
const setInError = (errors: Record<string, string>, innerError: ValidationError) => {
  return setIn(errors, innerError.path ?? '', innerError.message)
}

/**
 * Empty object map with no prototype. Used as default
 * value for reducing the `err.inner` array of errors
 * from a `yup~ValidationError`.
 */
const emptyObj: {} = Object.create(null)

/**
 * Takes a `yup` validation schema and returns a function that expects
 * a map of values to validate. If the validation passes, the function resolves to `undefined`
 * (signalling that the values are valid). If the validation doesn't pass, it resolves
 * to a map of invalid field names to errors.
 */

export function makeValidate<TValue = Record<string, unknown>>(
  schema: AnyObjectSchema
): (val: TValue) => Promise<undefined | Record<string, string>> {
  return async function validate(values: TValue) {
    try {
      await schema.validate(values, { abortEarly: false })
    } catch (err) {
      return (err as ValidationError).inner.reduce(setInError, emptyObj)
    }
  }
}

export function useValidationSchema<TValue = Record<string, unknown>>(
  schema: AnyObjectSchema
) {
  const validate = useMemo(() => makeValidate(schema), [schema])
  return validate
}

@Ksondzyk
Copy link

Ksondzyk commented Feb 18, 2023

const setInError = (errors: ValidationError, innerError: ValidationError): ValidationError =>
	<ValidationError>setIn(errors, innerError.path ?? '', innerError.message);

const emptyObj: ValidationError = Object.create(null);

export const makeValidate = (schema: ObjectSchema<AnyObject>) => {
	return async function validate(values: InferType<typeof schema>) {
		try {
			await schema.validate(values, { abortEarly: false });
		} catch (errors: unknown) {
			if (errors instanceof ValidationError) {
				return errors.inner.reduce(setInError, emptyObj);
			}
		}
	};
};

export const useValidationSchema = (schema: ObjectSchema<AnyObject>) => {
	return useMemo(() => makeValidate(schema), [schema]);
};

@mrasadatik
Copy link

TypeScript Users check out this gist.

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