DEV Community

Marco Pestrin
Marco Pestrin

Posted on • Edited on

Partial and complete validation of the form with Joi on React

Recently I started using Joi to validate forms on my React application.

In this code snippet I’ll show you how to do a partial and a full validation
The partial one when I’m typing, while the full validation when I click on the submit button.

Below here the syntax of the schema:

import Joi from 'joi';
import parsePhoneNumber from 'libphonenumber-js';

export default Joi.object().keys({
    'name': Joi.object({
        'text': Joi.string().required(),
        'voice': Joi.string().allow("")
    }).required(),
    'surname': Joi.object({
        'text': Joi.string().required(),
        'voice': Joi.string().allow("")
    }).required(),
    'phoneNumber': Joi.string().custom((phoneNumber, helper) => {
        const res = parsePhoneNumber(phoneNumber, 'IT');
        if (res !=== undefined && res.isValid()){
            return phoneNumber;
        }
        return helper.error('phoneNumber.invalid');
    }).required()
});
Enter fullscreen mode Exit fullscreen mode

Now I necessarily split the schema into more parts because I had to export different models from the file.
To make it more readable, the code becomes like this:

import Joi from 'joi';
import parsePhoneNumber from 'libphonenumber-js';

const isValidPhoneNumber = (phoneNumber, helper) => {
    const res = parsePhoneNumber(phoneNumber, 'IT');
    if (res !== undefined && res.isValid()){
        return phoneNumber;
    }
    return helper.error('phoneNumber.invalid');
};

const voiceSchema = Joi.object({
    'text': Joi.string().required(),
    'voice': Joi.string().allow("")
});

export const phoneNumberSchema = Joi.string().custom(isValidPhoneNumber).required();
export const nameSchema = voiceSchema.required();
export const surnameSchema = voiceSchema.required();

export default Joi.object().keys({
    'name': nameSchema,
    'surname': surnameSchema,
    'phoneNumber':phoneNumberSchema
});
Enter fullscreen mode Exit fullscreen mode

In the frontend it will look like this:

import React, { useState } from 'react';
import schema, { phoneNumberSchema, nameSchema, surnameSchema } from './schema2';

export const MyComponent = () => {

    const [ errors, setErrors ] = useState([]);

    function validationField(schema, value, field) {
        const err = JSON.parse(JSON.stringify(errors));
        const res = schema.validate(value);
        let errorsList = {};
        if (res.error) {
            res.error.details.forEach((error) => {
                errorsList[field] = error.message;
            });
            setErrors({
                ...errors,
                ...errorsList
            });
        } else {
            delete err[field];
            setErrors(err);
        }
    }

    function validationPaylod(payload) {
        const res = schema.validate(payload);
        if (res.error){
            // ko!
        } else {
            // ok!
        }
    }

    function handleChange(field, text, voice) {
        const payload = { 
            text,
            voice
        };
        switch (field) {
            case 'name':
                validationField(nameSchema, payload, 'name');
                break;
            case 'surname':
                validationField(surnameSchema, payload, 'surname');
                break;
            case 'phoneNumber':
                validationField(phoneNumberSchema, text, 'phoneNumber');
                break;
        }
    }

    return (<>{/* RENDER INSIDE HERE !!!! */}</>)
};
Enter fullscreen mode Exit fullscreen mode

In this case the handleChange function (with the three required parameters) will be called at the onChange event of the form and at the end we will call the validationPayload function.

Into my Errors state I have an object with all errors divided for keys.

Enjoy mates!

Top comments (0)