- Redux Form
- Examples
- Synchronous Validation Example
Synchronous Validation Example
To provide synchronous client-side validation, you will need to provide redux-form with a validation function that takes an object of form values and returns an object of errors. This is done by providing the validation function to the decorator as a config parameter, or to the decorated form component as a prop.
Additionally, you can provide redux-form with a warn function with the same type signature as your validation function. Warnings are errors that do not mark a form as invalid, allowing for two tiers of severity for errors.
The example validation function is purely for simplistic demonstration value. In your application, you will want to build some type of reusable system of validators.
Notice the reused stateless function component used to render each field. It is important that this not be defined inline (in the render()
function), because it will be created anew on every render and trigger a rerender for the field, because the component
prop will have changed.
IMPORTANT: Synchronous validation happens on every change to your form data, so, if your field value is invalid, your field.error value will always be present. You will probably only want to show validation errors once your field has been touched, a flag that is set for you by redux-form
when the onBlur event occurs on your field. When you submit the form, all the fields are marked as touched, allowing any of their validation errors to show.
Running this example locally
To run this example locally on your machine clone the redux-form
repository, then cd redux-form
to change to the repo directory, and run npm install
.
Then run npm run example:syncValidation
or manually run the following commands:
cd ./examples/syncValidation
npm install
npm start
Form
Values
undefined
Code
SyncValidationForm.js
import React from 'react'
import { Field, reduxForm } from 'redux-form'
const validate = values => {
const errors = {}
if (!values.username) {
errors.username = 'Required'
} else if (values.username.length > 15) {
errors.username = 'Must be 15 characters or less'
}
if (!values.email) {
errors.email = 'Required'
} else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
errors.email = 'Invalid email address'
}
if (!values.age) {
errors.age = 'Required'
} else if (isNaN(Number(values.age))) {
errors.age = 'Must be a number'
} else if (Number(values.age) < 18) {
errors.age = 'Sorry, you must be at least 18 years old'
}
return errors
}
const warn = values => {
const warnings = {}
if (values.age < 19) {
warnings.age = 'Hmm, you seem a bit young...'
}
return warnings
}
const renderField = ({ input, label, type, meta: { touched, error, warning } }) => (
<div>
<label>{label}</label>
<div>
<input {...input} placeholder={label} type={type}/>
{touched && ((error && <span>{error}</span>) || (warning && <span>{warning}</span>))}
</div>
</div>
)
const SyncValidationForm = (props) => {
const { handleSubmit, pristine, reset, submitting } = props
return (
<form onSubmit={handleSubmit}>
<Field name="username" type="text" component={renderField} label="Username"/>
<Field name="email" type="email" component={renderField} label="Email"/>
<Field name="age" type="number" component={renderField} label="Age"/>
<div>
<button type="submit" disabled={submitting}>Submit</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>Clear Values</button>
</div>
</form>
)
}
export default reduxForm({
form: 'syncValidation', // a unique identifier for this form
validate, // <--- validation function given to redux-form
warn // <--- warning function given to redux-form
})(SyncValidationForm)