Form validation in React

Form validation is done using my useForm hook. Validation is supported:

  • required – it checks if the form field contains any data
  • isEmail – whether the entered data is a valid email address
  • min – if entered data has the appropriate number of characters
  • match – it is checked whether the entered data is the same as in another form field (whether the password and password repetition are the same)

To use a hook in a component containing a form:

const {values, errors, validate, handleSubmit} = useForm(callback)

The values ​​passed from the hook are:

  • values ​​- an object containing values ​​entered into the form and (after validation) passed to the callback() function, which queries the external API
  • errors – an object containing validation error values ​​that will be displayed under the form fields
  • validate – a function that validates the entered values ​​while typing
  • handleSubmit – validation function when submitting the form (onSubmit)

In the form, we pass the event object as the first argument, and the list of form element names as the second argument, i.e.

<form onSubmit={(event) => 
            handleSubmit(event, ['role', 'username', 'email', 'pass', 'passConfirm'])}>

An example form element named passConfirm looks like this:

<div>
  <input name='passConfirm' 
           placeholder='Retype Password' 
           type='password' 
           value={values.passConfirm || ''}
           onChange={(event) => validate( event, {'match' : 'pass', 'required': true})}/>
  <p className='help is-danger'>{errors.passConfirm}</p>
</div>

Hook useForm.js

import { useState } from 'react'

function useForm(callback) {
    
    const [values, setValues] = useState({})
    const [errors, setErrors] = useState({})

    const pattern = new RegExp(
        /^[a-zA-Z0-9]+@(?:[a-zA-Z0-9]+\.)+[A-Za-z]+$/
    )

At the beginning, the following objects are initialized: values ​​- storing the values ​​entered into the form fields and errors – storing error messages. Then a RegExp object is created that holds a pattern that matches a valid email address.

The useForm function takes as an argument the name of the callback function that will be called after submitting the form and executing the handleSubmit function:

    const handleSubmit = (event, controls) => {
        event.preventDefault()
        controls.map((value) => validateOnSubmit(value))
        setErrors({...errors})
        if (!Object.keys(errors).length) {
            callback()
        }
    }

The above function takes an event object and a list of form fields as arguments. Next, the preventDefault() function is called to prevent the default action when submitting the form, and then the validateOnSubmit() function is called for each form field, i.e.

   const validateOnSubmit = (value) => {
        if (values[value] === undefined) {
            errors[[value]] =  'This field is required'
        }
    }

The above function checks if any values ​​are entered in the form field. If not error is generated.

If the errors object is not empty, i.e. errors are present, the callback function will not be executed.

The validate() function validates the text entered into the form field as you type, and displays appropriate validation error messages as you type. Takes an event object and an object containing validation rules as arguments.

const validate = (event, rules) => {

   setValues(values => ({...values, [event.target.name]: event.target.value}))

For any form field, the setValues() function is called, which completes the values ​​object with an object with the key of the form field into which the text is currently being entered. Then, individual validations are called for specific rules, i.e.

  • If the ‘required’:true rule in the form is selected, the hook checks whether it is dealing with this method, and whether the value of the text entered into the form field is equal to 0. In this case, the errors object is supplemented with another element with the key being the name of the form field .
// is required validation
        if (rules.required === true && event.target.value.length === 0) {
            setErrors(errors => ({...errors, [event.target.name]: 'This field is required'}))
        } 
  • If the selected rule is ‘isEmail’:true in the form, then the hook checks whether the entered text is consistent with the pattern, i.e.
// is valid email address validation
        else if (rules.isEmail === true && !pattern.test(event.target.value)) {
            setErrors(errors => ({...errors, [event.target.name]: 'This email address is invalid'}))
        }
  • If the selected rule is ‘min’: number, then the hook checks if the text entered in the form field is at least the length given as the number value, e.g. ‘min’: 6 will accept text with a length of at least 6 characters. Otherwise, an appropriate error will be set. The name of the form field is downloaded to the text of the error message, i.e. the text will change depending on the name of the variable.
// min value length validation
        else if (rules.min && event.target.value.length < rules.min) {
            setErrors(errors => ({...errors, 
                [event.target.name]:
                [event.target.name.charAt(0).toUpperCase()] 
                + [event.target.name.slice(1)]
                + '  is too short'}))
        } 
  • If the selected rule is match, then as the value for the match key we enter the name of the form field with which the values ​​are to be compared. Hook does not have the fixed name of the compared field, i.e.
 // match validation
        else if (rules.match && event.target.value!==values[rules.match]) {
            setErrors(errors => ({...errors, 
                [event.target.name]: "Passwords don't match"}))
        } 

If none of the above conditions are met, the else block is invoked:

else {
            delete errors[event.target.name]
        }

Leave a Reply

Your email address will not be published. Required fields are marked *

+ sixty four = sixty six