Home > References > Web Development > Reactjs references

5 Ways to submit a form in Reactjs

Last updated : 24 April 2022

This article discusses five ways to submit a form in Reactjs. Each method prepares an Html form data object for submission. We use different ways to maintain the form data using state management in each method we discuss.

Feel free to select a suitable submission method depending on your requirements. Below is an example of sending the form object to the server as a JSON response.

fetch('saveContact/', {
	method: 'POST',
	body: JSON.stringify(responseBody),
	headers: {
	  'Content-Type': 'application/json'
	},
})

1. Reactjs form submit with multiple useState hooks

In this method, we maintain each form input element value in a separate state variable. That is the simplest out of the five different methods we discuss. Suitable for simple forms with a few input elements that need simple form validation.

import { useState } from "react"

export const Form = () => {
    const [firstName, setFirstName] = useState("")
    const [lastName, setLastName] = useState("")
    const [age, setAge] = useState("")

    interface FormDataType {firstName:string, lastName: string, age: string}
    const responseBody: FormDataType = {firstName: "", lastName: "", age: "0"}

    const onSubmitHandler = (event: React.FormEvent<HTMLFormElement>) => {
        event.preventDefault();
        responseBody.firstName = firstName
        responseBody.lastName = lastName
        responseBody.age = age
        console.log(JSON.stringify(responseBody))
	//Form submission happens here
    }
    const inputChangeHandler = (setFunction: React.Dispatch<React.SetStateAction<string>>, event: React.ChangeEvent<HTMLInputElement>) => {
        setFunction(event.target.value)
    }
  
    return(
        <form onSubmit={onSubmitHandler}>
            <div><label htmlFor="first_name">First Name</label></div>
            <div><input id="first_name" onChange={(e)=>inputChangeHandler(setFirstName, e)} type="text"/></div>
            <div><label htmlFor="last_name">Last Name</label></div>
            <div><input id="last_name" onChange={(e)=>inputChangeHandler(setLastName, e)} type="text"/></div>
            <div><label htmlFor="age">Age</label></div>
            <div><input id="age" onChange={(e)=>inputChangeHandler(setAge, e)} type="number"/></div>
            <input type="submit"/>
        </form>
    )
}

2. Reactjs form submit with single useState hook

Here we store all the form input data in a single state variable. To achieve that, we have to build an object with properties to represent all the input elements in the form. This object can be stored in a state variable. Each input change updates the relevant property of the object stored in the state variable.

import { useState } from "react"

export const Form = () => {
    interface FormDataType {firstName:string, lastName: string, age: string}
    const formData: FormDataType = {firstName: "", lastName: "", age: ""}
    const [responseBody, setResponseBody] = useState<FormDataType>(formData)

    const inputChangeHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
        const {name, value} = event.target
        setResponseBody({...responseBody, [name]: value})
    }
    const onSubmitHandler = (event: React.FormEvent<HTMLFormElement>) => {
        event.preventDefault()
        console.log(responseBody)
	//Form submission happens here
    }
    return(
        <form onSubmit={onSubmitHandler}>
            <div><label htmlFor="firstName">First Name</label></div>
            <div><input id="firstName" name="firstName" onChange={(e)=>inputChangeHandler(e)} type="text"/></div>
            <div><label htmlFor="lastName">Last Name</label></div>
            <div><input id="lastName" name="lastName" onChange={(e)=>inputChangeHandler(e)} type="text"/></div>
            <div><label htmlFor="age">Age</label></div>
            <div><input id="age" name="age" onChange={(e)=>inputChangeHandler(e)} type="number"/></div>
            <input type="submit"/>
        </form>
    )
}

3. Using useRef to maintain form element values

The useRef can maintain a reference to the form element's dom. Since the useRef directly accesses the real dom, it is not advised to use useRef to do any direct dom manipulations. However, in our case, we only read from the dom, therefore, no performance impact is anticipated.

import { useRef } from "react"

export const Form = () => {
    const inputFirstName = useRef<HTMLInputElement>(null)
    const inputLastName = useRef<HTMLInputElement>(null)
    const inputAge = useRef<HTMLInputElement>(null)

    interface FormDataType {firstName:string, lastName: string, age: string}
    const formData: FormDataType = {firstName: "", lastName: "", age: ""}

    const onSubmitHandler = (event: React.FormEvent<HTMLFormElement>) => {
        event.preventDefault();
        formData.firstName = inputFirstName?.current?.value||""
        formData.lastName = inputLastName?.current?.value||""
        formData.age = inputAge?.current?.value||""
        console.log(formData)
	//Form submission happens here
    }

    return(
        <form onSubmit={onSubmitHandler}>
            <div><label htmlFor="first_name">First Name</label></div>
            <div><input id="first_name" ref={inputFirstName} type="text"/></div>
            <div><label htmlFor="last_name">Last Name</label></div>
            <div><input id="last_name" ref={inputLastName} type="text"/></div>
            <div><label htmlFor="age">Age</label></div>
            <div><input id="age" ref={inputAge} type="number"/></div>
            <input type="submit"/>
        </form>
    )
}

4. Using FormData to read Html Form elements

In this method, we cast the HtmlForm element to the FormData object. Then we iterate the FormData object and create the responseBody with key and value pairs with respective Html element names and values. For more details about FormObject, check the Mozilla Developer site.

export const Form = () => {

    interface formDataType {[key:string]: FormDataEntryValue}
    const responseBody: formDataType = {}

    const inputChangeHandler = (event: React.FormEvent<HTMLFormElement>) => {
        event.preventDefault()
        const formData = new FormData(event.currentTarget as HTMLFormElement)
        formData.forEach((value, property:string) => responseBody[property] = value);
        console.log(JSON.stringify(responseBody))
	//Form submission happens here
    }
    return(
        <form onSubmit={inputChangeHandler}>
            <div><label htmlFor="firstName">First Name</label></div>
            <div><input id="firstName" name="firstName" type="text"/></div>
            <div><label htmlFor="lastName">Last Name</label></div>
            <div><input id="lastName" name="lastName" type="text"/></div>
            <div><label htmlFor="age">Age</label></div>
            <div><input id="age" name="age" type="number"/></div>
            <input type="submit"/>
        </form>
    )
}

5. Using useReducer to perform advanced validation on Reactjs forms

If your form needs complex validation and pre-submit processing logic, useReducer can be a better option. Using useReducer is a bit complex, but it offers a few advantages when accessing the form state. If you need to access the previous state of a variable, useReducer state provides safer access to the previous state. useReducer provides safer access to a shared state where you can perform validation and any pre-processing logic.

import { useReducer } from "react"

type FormState = {
    firstName: string
    lastName: string
    age: string
}
type FormAction = {
    type: string
    payLoad: string
}
const initialState: FormState = {
    firstName: "",
    lastName: "",
    age: "",
}
type FormValidityState = {
    firstNameError: boolean
    lastNameError: boolean
    ageError: boolean
}
const initialValidityState: FormValidityState = {
    firstNameError: false,
    lastNameError: false,
    ageError: false
}
type FormValidityAction = {
    type: string
    payLoad: FormState
}

const formReducer = (state: FormState, action: FormAction): FormState => {
    switch(action.type){
        case "UPDATE_FIRST_NAME": return{
            ...state, firstName: action.payLoad, 
        }
        case "UPDATE_LAST_NAME": return{
            ...state,lastName: action.payLoad, 
        }
        case "UPDATE_AGE": return{
            ...state, age: action.payLoad, 
        }
        default:
            return state
    }
}

const formValidityReducer = (state: FormValidityState, action: FormValidityAction): FormValidityState => {
    switch(action.type){
        case "VALIDATE_FIRST_NAME": return{
            ...state,
            ...({firstNameError: action.payLoad.firstName.length > 0 ? false: true})
        }
        case "VALIDATE_LAST_NAME": return{
            ...state,
            ...({lastNameError: action.payLoad.lastName.length > 0 ? false: true})
        }
        case "VALIDATE_AGE": return{
            ...state,
            ...({ageError: action.payLoad.age.length > 0 ? false: true})
        }
    default:
        return state
    }
}

export const Form = () => {

    const [formData, setFormData] = useReducer(formReducer, initialState)
    const [formValidityData, setFormValidityData] = useReducer(formValidityReducer, initialValidityState)

    const onButtonPress = (event: React.FormEvent) => {
        event.preventDefault()
        console.log(formData)
        //Form submission happens here
    }
    return(
        <form onSubmit={onButtonPress}>
            <div><label htmlFor="first_name">First Name</label></div>
            <div>
                <input 
                id="first_name" 
                style={{backgroundColor:formValidityData.firstNameError ?"pink" : ""}} 
                onChange={(e) =>setFormData({type:"UPDATE_FIRST_NAME", payLoad:e.target.value})}
                onBlur={(e) => setFormValidityData({type: "VALIDATE_FIRST_NAME", payLoad: formData})}
                type="text"/>
            </div>
            <div><label htmlFor="last_name">Last Name</label></div>
            <div>
                <input id="last_name" 
                style={{backgroundColor:formValidityData.lastNameError ? "pink" : ""}} 
                onChange={(e) =>setFormData({type:"UPDATE_LAST_NAME", payLoad:e.target.value})}
                onBlur={(e) => setFormValidityData({type: "VALIDATE_LAST_NAME", payLoad: formData})}
                type="text"/>
            </div>
            <div><label htmlFor="age">Age</label></div>
            <div>
                <input 
                id="age" 
                style={{backgroundColor:formValidityData.ageError ? "pink" : ""}} 
                onChange={(e) =>setFormData({type:"UPDATE_AGE", payLoad:e.target.value})} 
                onBlur={(e) => setFormValidityData({type: "VALIDATE_AGE", payLoad: formData})}
                type="number"/>
            </div>
            <input disabled={formValidityData.firstNameError===true || formValidityData.lastNameError===true || formValidityData.ageError===true} type="submit"/>
        </form>
    )
}
Lance
By: Lance
Lance is a software engineer with over 15 years of experience in full-stack software development.
Read more...

Comments are disabled

No Comments