Synchronous Validation Example

There are two ways to provide synchronous client-side validation to your form.

The first is 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.

The second is to use individual validators for each field. See Field-Level Validation Example.

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: If the validation function returns errors and the form does not currently render fields for all of the errors, then the form will be considered valid and will be submitted.

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.

IMPORTANT: In your validate function, values can be undefined, so pay attention when you are validating nested fields. If not, you could encounter some TypeError: undefined is not an object.

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)