Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Validators
'use strict';
/**
* Validators are functions accepting `state` and `prop` where `state` is an object
* containing the entire form state, e.g. `{ email: 'email', password: 'password' }`
* and `prop` refers to the property on `state` to be validated.
*
* Validators are expected to return a falsy value if valid and a truthy value if
* invalid. This is to allow information re: the error to be returned. e.g.
*
* // Validator to ensure `name` contains the letter 'a'.
* const validateName = (state, prop) => state[prop] && !state[prop].includes('a') ? `${prop} must include the letter 'a'` : void 0;
* validateName({ name: 'You' }, 'name'); // name must include the letter 'a'
* validateName({ name: 'a' }, 'name'); // undefined
*
* NOTE: The entire form state is passed in to allow validators to compute a result
* based on other form values, e.g. a confirm email field could check the original
* email value.
*/
/**
* Allows multiple validator functions to be composed into a single function.
* e.g.
*
* // Order matters
* const validateRequiredName = combineValidators(
* (state, prop) => !state[prop] ? `${prop} is required` : void 0,
* (state, prop) => state[prop] && !state[prop].includes('a') ? `${prop} must include the letter 'a'` : void 0
* )
*
* validateRequiredName({ name: '' }, 'name'); // name is required
* validateRequiredName({ name: 'You' }, 'name'); // name must include the letter 'a'
* validateRequiredName({ name: 'a' }, 'name'); // undefined
*/
export function combineValidators(...validators) {
return (state, prop) => {
// NOTE: This is probably 'functional' for the sake of it...i.e. the loop
// would ideally break as soon as it finds an error.
return validators.reduce((acc, validator) => {
return acc ? acc : validator(state, prop);
}, '');
}
}
/**
* Validator creators
* These are just helper functions to generate common validator functions
* returning custom error messages. e.g.
*
* const validateName = required('Your name is required');
* validateName({ name: '' }, 'name'); // Your name is required
* validateName({ name: 'You' }, 'name'); // undefined
*
* Or with `combineValidators`:
*
* const validateName = combineValidators(
* required('Your name is required'),
* minLength(4, 'Your name must contain more than 4 characters'),
* maxLength(10, 'Your name must contain less than 20 characters')
* );
* validateName({ name: '' }, 'name'); // Your name is required
* validateName({ name: 'You', 'name' }); // Your name must contain more than 4 characters
* validateName({ name: 'SomebodyElse', 'name' }); // Your name must contain less than 10 characters
* validateName({ name: 'Somebody', 'name' }); // undefined
*/
export function required(message) {
return (state, prop) => !state[prop] ? message : void 0;
}
export function minLength(min, message) {
return (state, prop) => state[prop] && state[prop].length < min ? message : void 0;
}
export function maxLength(max, message) {
return (state, prop) => state[prop] && state[prop].length > max ? message : void 0;
}
export function regex(regex, message) {
return (state, prop) => state[prop] && !regex.test(state[prop]) ? message : void 0;
}
let emailRegex = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
export function email(message) {
return regex(emailRegex, message);
}
@richardscarrott

This comment has been minimized.

Copy link
Owner Author

richardscarrott commented Jan 22, 2016

This is the end user api:

const validateName = combineValidators(
    required('Your name is required'),
    minLength(4, 'Your name must contain more than 4 characters'),
    maxLength(10, 'Your name must contain less than 20 characters')
);

validateName({ name: 'Somebody' }, 'name'); // NOTE: the validator signature is a bit ugly but would be abstracted away in a React component for example
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.