Skip to content

Instantly share code, notes, and snippets.

@chris-kobrzak
Created December 18, 2017 12:21
Show Gist options
  • Save chris-kobrzak/2a730145deb2775094201bb269967e56 to your computer and use it in GitHub Desktop.
Save chris-kobrzak/2a730145deb2775094201bb269967e56 to your computer and use it in GitHub Desktop.
Form validation logic that can be plugged to React event handlers
// Low-level, generic validation functions
// Signature: any => boolean
const hasLength = value => value.length > 0
const hasLettersAndDotsOnly = value => (
value.match(/^[a-z\.]+$/gi) !== null
)
/*
Map input fields to "types" that we want to validate in a certain way. This
will become more useful e.g. when we have two address forms on a page (and
presumably apply the same rules to them).
The values are referenced in inputTypeToValidations's top level keys.
*/
const inputTypeToName = {
personTitle: 'title',
firstName: 'firstName',
lastName: 'lastName'
}
// Generic validation messages store
const message = {
specialCharacter: 'You can only use letters here'
}
const inputTypeToValidations = {
// Array here as the order of validation messages matters
title: [
{ hasLength: 'Please enter your title' },
{ hasLettersAndDotsOnly: message.specialCharacter }
]
}
// hasLength references validation message keys in `inputTypeToValidations` above
const validationRuleToMethod = {
hasLength,
hasLettersAndDotsOnly
}
function validateInput (name, value) {
// Methods or functions instead of dictionaries?
const inputType = inputTypeToName[name]
const validations = inputTypeToValidations[inputType]
for (let validation of validations) {
const [ rule ] = Object.keys(validation)
const validate = validationRuleToMethod[rule]
if (! validate) {
continue
}
// The actual validation method call
const valid = validate(value)
if (valid) {
// Only concerned with the first validation error
continue
}
return { valid, rule, message: validation[rule] }
}
return { valid: true }
}
console.log(
validateInput('personTitle', ''),
validateInput('personTitle', '_'),
validateInput('personTitle', 'Mrs.')
)
@chris-kobrzak
Copy link
Author

chris-kobrzak commented Dec 18, 2017

This is a simple form validation collaborator pattern that can be plugged into React components via onBlur, onChange and onClick handlers. You can pass the validation results to a flux (Redux?) store, making reducers and the store itself completely decoupled from the actual form validation logic.

The version above does not have a concept of dependencies between different form fields but this could be achieved by e.g. a higher-level validation strategy pattern and extending validateInput to accept extra parameters containing validation rules that should be ignored or added.

Brief description of the concepts and patterns in use

  1. At the lowest level we have simple validator functions that accept a value and return a boolean. They can be extended to accept additional parameters e.g. by means of parameter binding.
  2. Validator functions are linked to validation rules that are string constants. This makes them super portable as you can e.g. use the same interface on the back-end and front-end side of things. See validationRuleToMethod.
  3. Multiple validation rules are ordered in a certain way and linked to form input names. There is an additional mapping to the actual validation messages intended for the UI but this could be broken down into smaller units (see inputTypeToValidations).
  4. Form input types are linked to input names that are expected as input by the main validateInput function. This is a layer that might not be needed for simpler forms where all field names are unique and follow distinct validation rules.

Implementation overview

You might want to implement this validation pattern inside of the form control event handlers. All you need to do is call validateInput and pass results of the call to Redux and get a reducer to store them against a particular field.

Presentational React components then render any necessary validation messages as per the current Redux store state (see a screen grab example below).

form state in redux

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