October 21, 2020

Creating a react dynamic form from JSON

Today, we will be going to implement creating a react dynamic form from JSON

Dynamic form in the sense, we will get an array of objects. On the basis of the individual object, we will render form fields

Please subscribe for web development tutorials

for eg.

  {
            id: 'zipCode',
            type: 'input',
            placeholder: 'Enter zipcode',
            config: {
                dataType: 'text',
                placeholder: 'ZIP Code'
            },
            value: '',
            validation: {
                required: true,
                minLength: 5,
                maxLength: 5,
            },
            valid: false,
            errorMessage: 'Enter Valid data',
            touched: false
        }

In the above object, contains a property which helps us to understand which form field we should render

like, each property denotes have some use cases, In above example type is input, so it helps to identify we need to render input field, In config, we have dataType property which helps to understand we need to render below field

<input type=text />

So, let’s develop below sample user details form using react dynamically

Please find code below

Here table of contents,

  • Setting up a react project
  • Creating a JSON
  • Creating a UserForm container component
  • Creating individual field component

Setting up a react project

we will use below command to setup starter react project

npx create-react-app dynamic-form-using-react

Above, will help you to setup starter react project, after this your folder structure will look like this

Creating a json

Here is a json below which helps to render a user form

[
         {
            id: 'name',
            type: 'input',
            placeholder: 'Enter name',
            config: {
                dataType: 'text',
                placeholder: 'Your Name'
            },
            value: '',
            validation: {
                required: true
            },
            valid: false, 
            errorMessage: 'Enter Valid data',
            touched: false
        },
           {
            id: 'street',
            type: 'textarea',
            placeholder: 'Enter Address',
            config: {
                dataType: 'text',
                placeholder: 'Street'
            },
            value: '',
            validation: {
                required: true
            },
            valid: false,
            errorMessage: 'Enter Valid data',
            touched: false
        },
         {
            id: 'zipCode',
            type: 'input',
            placeholder: 'Enter zipcode',
            config: {
                dataType: 'text',
                placeholder: 'ZIP Code'
            },
            value: '',
            validation: {
                required: true,
                minLength: 5,
                maxLength: 5,
            },
            valid: false,
            errorMessage: 'Enter Valid data',
            touched: false
        },
        {
            id: 'country',
            type: 'select',
            placeholder: 'Enter country',
            config: {
                dataType: 'text',
                placeholder: 'Country'
            },
            options: [
                {
                    value: 'USA',
                    displayValue: 'United state of america'
                },
                {
                    value: 'UK',
                    displayValue: 'United kingdom'
                },
                {
                    value: 'IN',
                    displayValue: 'India'
                }
            ],
            value: '',
            validation: {
                required: true
            },
            valid: false,
            errorMessage: 'Enter Valid data',
            touched: false
        },
         {
            id: 'email',
            type: 'input',
            placeholder: 'Enter email',
            config: {
                dataType: 'email',
                placeholder: 'Your E-Mail'
            },
            value: '',
            validation: {
                required: true,
                isEmail: true
            },
            valid: false,
            errorMessage: 'Enter Valid data',
            touched: false
        },
    ]

Above is array of object, where every object represents individual form field

Creating a field component

import React from 'react';

const field = (props) => {
    const fieldConfig = props.fieldConfig;
    let element = null;
    let classes = ['field'];
    if (fieldConfig.touched && !fieldConfig.valid) {
        classes.push('invalid');
    }

    switch (fieldConfig.type) {
        case 'input':
            element = (<input type={fieldConfig.config.type}
                name={fieldConfig.id}
                value={fieldConfig.value}
                className={classes.join(' ')}
                placeholder={fieldConfig.placeholder}
                required={fieldConfig.validation.required}
                onBlur={props.focused}
                minLength={fieldConfig.validation.minLength}
                onChange={props.changed}
                maxLength={fieldConfig.validation.maxLength}
            />);
            break;
            case 'textarea':
                element = <textarea
                value={fieldConfig.value}
                className={classes.join(' ')}
                placeholder={fieldConfig.placeholder}
                required={fieldConfig.validation.required}
                onBlur={props.focused}
                minLength={fieldConfig.validation.minLength}
                onChange={props.changed}
                maxLength={fieldConfig.validation.maxLength}   
                />;
                break;
            case 'select':
                element = (
                    <select
                    value={fieldConfig.value}
                    className={classes.join(' ')}
                    required={fieldConfig.validation.required}
                    onBlur={props.blured}
                    onChange={props.changed}>
                        <option>{fieldConfig.placeholder}</option>
                        {fieldConfig.options.map(option => (
                            <option key={option.value} value={option.value}>
                                {option.displayValue}
                            </option>
                        ))}
                    </select>
                );
                break;
    }

    return (
        <div className="field-wrapper">
           {element}
        </div>
    )
}

export default field;

In the above example, As you can see we have a switch case in which on the basis of the type we are rendering a field and we passed individual JSON object to this component.

If type is text then input field will be rendered, for textarea type textarea field will be rendered, same will happen for select,

I have just added three types of form fields, you can add a checkbox, radio, etc fields.

we have also added attributes to the element for eg, placeholder, minlength, maxlength, required.

and event handlers also like onblur and onchange

Creating a userForm container

Above we have developed individual field component, let’ use that component

import React, { Component } from 'react';
import Field from '../../components/field/field'
class Form extends Component {
    state = {
        fields: [
            {
                id: 'name',
                type: 'input',
                placeholder: 'Enter name',
                config: {
                    dataType: 'text',
                    placeholder: 'Your Name'
                },
                value: '',
                validation: {
                    required: true
                },
                valid: false,
                errorMessage: 'Enter Valid data',
                touched: false
            },
            {
                id: 'street',
                type: 'textarea',
                placeholder: 'Enter Address',
                config: {
                    dataType: 'text',
                    placeholder: 'Street'
                },
                value: '',
                validation: {
                    required: true
                },
                valid: false,
                errorMessage: 'Enter Valid data',
                touched: false
            },
            {
                id: 'zipCode',
                type: 'input',
                placeholder: 'Enter zipcode',
                config: {
                    dataType: 'text',
                    placeholder: 'ZIP Code'
                },
                value: '',
                validation: {
                    required: true,
                    minLength: 5,
                    maxLength: 5,
                },
                valid: false,
                errorMessage: 'Enter Valid data',
                touched: false
            },
            {
                id: 'country',
                type: 'select',
                placeholder: 'Enter country',
                config: {
                    dataType: 'text',
                    placeholder: 'Country'
                },
                options: [
                    {
                        value: 'USA',
                        displayValue: 'United state of america'
                    },
                    {
                        value: 'UK',
                        displayValue: 'United kingdom'
                    },
                    {
                        value: 'IN',
                        displayValue: 'India'
                    }
                ],
                value: '',
                validation: {
                    required: true
                },
                valid: false,
                errorMessage: 'Enter Valid data',
                touched: false
            },
            {
                id: 'email',
                type: 'input',
                placeholder: 'Enter email',
                config: {
                    dataType: 'email',
                    placeholder: 'Your E-Mail'
                },
                value: '',
                validation: {
                    required: true,
                    isEmail: true
                },
                valid: false,
                errorMessage: 'Enter Valid data',
                touched: false
            },
        ],
        formValid: false
    }

    fieldChange = (event, field, index) => {
        const updatedField = { ...field };
        updatedField.value = event.target.value;
        updatedField.valid = this.checkValidity(updatedField);

        const updatedFields = [...this.state.fields];
        updatedFields.splice(index, 1, updatedField);
        let formValid = true;
        for (let field of updatedFields) {
            if (!field.valid) {
                formValid = false;
            }
        }
        this.setState({
            fields: updatedFields,
            formValid: formValid
        })
    }


    checkValidity = (field) => {
        const rules = field.validation;
        const value = field.value;
        let isValid = true;
        if (!rules) {
            return true;
        }
        if (rules.required) {
            isValid = value.trim() !== '' && isValid;
        }
        if (rules.minLength) {
            isValid = value.length >= rules.minLength && isValid
        }
        if (rules.maxLength) {
            isValid = value.length <= rules.maxLength && isValid
        }
        if (rules.pattern) {
            isValid = rules.pattern.test(value) && isValid
        }
        return isValid;
    }

    fieldBlur(event, field, index) {
        if (field.touched) {
            return;
        }
        const updatedField = { ...field };
        updatedField.touched = true;
        updatedField.valid = this.checkValidity(updatedField);
        const updatedFields = [...this.state.fields];
        updatedFields.splice(index, 1, updatedField);

        this.setState({
            fields: updatedFields,
        })
    }

    onSubmit = (event) => {
        event.preventDefault();
        alert('data submitted')
    }

    render() {
        return (<form onSubmit={(event) => this.onSubmit(event)}>
            {this.state.fields.map((field, index) => {
                return <Field
                    key={field.id}
                    fieldConfig={field}
                    blured={(event) => this.fieldBlur(event,field,index)}
                    changed={(event) => this.fieldChange(event,field,index)} />
            })}
            <button type='submit' 
               disabled=!this.state.formValid}>
               Submit
             </button>
        </form>)

    }
}


export default Form;

In the above code, as you can see we have rendering fields array and mapping it to Field component and passing individual field as a prop to the component

also passed change and blur event,

fieldChange – method is responsible for validating individual field using checkValidity function where on the basis of config object like is it required or not, does it have minlength or maxlength and also tests pattern.

Note: we are using onBlur event for knowing that field is touched or not and if touched then only we are checking validity .

Conclusion:

This is the basic form we have developed , you can alter this with your need.Hope so you guys got some idea to develop forms dynamically using react.

If you like this tutorial, follow me on twitter for more update.

Please subscribe for web development tutorials

Leave a Reply

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