Just like any other web application, in React Js, the primary ways to interact with users are through forms and user input. In this article, I will guide you through handling user input and forms in React JS with detailed explanations and code examples.
Introduction to Forms in React
In HTML, form elements such as <input>
, <textarea>
, and <select>
typically maintain their own state, which they update based on user input.
In React Js, we use the component state to hold the HTML element's states. Then we use the element's event handlers to sync any changes to the element with the corresponding state variable.
This approach is known as "controlled components." In a controlled component, the form data is handled by the React component (instead of the DOM) and kept in the local state.
Creating a Simple Form
Let's start by creating a simple form with an input field for a user's name:
import { useState } from "react"
export const Input = () => {
const [value, setValue] = useState()
const handleInputChange = (event) => {
setValue(event.target.value)
}
const handleFormSubmit = (event) => {
alert("You entered : " + value)
event.preventDefault()
}
return (
<form onSubmit={handleFormSubmit}>
<label>
Name: <input type="text" value={value} onChange={handleInputChange} />
</label>
<input type="submit" value="Submit"/>
</form>
)
}
In the above example, the Input
component's state is updated whenever the user types in the text box. The onChange
in the input element attribute calls the handleInputChange
function.
When the form is submitted, it calls the handleFormSubmit
function. The preventDefault
in the handleFormSubmit
function prevents the default form submission and instead displays an alert with the submitted name.
Handling Multiple Inputs
When handling multiple controlled input elements, you can add a name
attribute to each element and let the handler function choose what to do based on the value of event.target.name
.
import { useState } from "react"
export const SignupForm = () => {
const state = {name: '', subscribe: false}
const [value, setValue] = useState(state)
const handleInputChange = (event) => {
const target = event.target
const value = target.type === 'checkbox' ? target.checked : target.value
setValue((oldValue) => {
return {...oldValue, [target.name]: value}
})
}
const handleFormSubmit = (event) => {
alert(value.name + " -> " + value.subscribe)
event.preventDefault()
}
return (
<form onSubmit={handleFormSubmit}>
<label>
Name: <input type="text" name="name" value={value.name} onChange={handleInputChange} />
</label>
<label>
Subscribe: <input type="checkbox" name="subscribe" checked={value.subscribe} onChange={handleInputChange} />
</label>
<input type="submit" value="Submit"/>
</form>
)
}
In this example, the SignupForm
component handles an input checkbox and an input text. The handleInputChange
function is shared among both inputs and uses the name attribute to update the respective state property.
The component state is an object with matching properties to input names. It is important to note that those state properties must match the input names.
Also, please pay attention to how I update the state in every input change. The setValue
method accepts the old state value as an argument and appends the new state changes.
You can find more information on this at How to update nested state objects in Reactjs.
Validating Form Input
Form validation is crucial to prevent invalid data submission. Let's modify the previous example and include some form validation.
import { useState } from "react"
export const SignupForm = () => {
const state = {name: '', subscribe: false, errorMessage: ''}
const [value, setValue] = useState(state)
const handleInputChange = (event) => {
const target = event.target
const value = target.type === 'checkbox' ? target.checked : target.value
setValue((oldValue) => {
return {...oldValue, [target.name]: value, errorMessage: validateInput(target)}
})
}
const validateInput = (target) => {
if (target.type !== 'checkbox' && /\d/.test(target.value)) {
return 'Name should not contain numbers'
}
}
const handleFormSubmit = (event) => {
if(!value.errorMessage){
alert("Form submitted")
}
event.preventDefault()
}
return (
<form onSubmit={handleFormSubmit}>
{value.errorMessage && <p>{value.errorMessage}</p>}
<label>
Name: <input type="text" name="name" value={value.name} onChange={handleInputChange} />
</label>
<label>
Subscribe: <input type="checkbox" name="subscribe" checked={value.subscribe} onChange={handleInputChange} />
</label>
<input type="submit" value="Submit"/>
</form>
)
}
In the above example, I validate the name to avoid any digits. If the name contains digits, a message is displayed on the top, preventing the user from submitting the form.
Handling Form Submission
Typically, form data is sent to a server when a form is submitted. That is done using either a GET
request or a POST
request. In React, we prevent the default form submission and handle the submission in JavaScript.
That prevents refreshing the page after form submission. Here's an example of how to handle a form submission using the Fetch API:
const handleFormSubmit = (event) => {
fetch('saveSubscription/', {
method: 'POST',
body: JSON.stringify(value),
headers: {
'Content-Type': 'application/json'
},
})
event.preventDefault()
}
Note that I Stringify the state value using JSON.stringify(value)
. For more details on different ways to submit forms in React Js, refer to 5 Ways to submit a form in Reactjs.