In this article, I will demonstrate how I build my Upload Form in my React project using Controlled Components and Sign Up Form with Uncontrolled Component.
First, let's illustrate the difference between the two methods to create Forms: the HTML way, with Uncontrolled Components and the best practice there is for React with Controlled Components.
Uncontrolled Components
Uncontrolled Components are the form data that is handled by the DOM itself. These components are not controlled by React state.
The values of the form elements are traditionally controlled by and stored on the DOM. Here is an example of a Sign Up form from my project:
import React from "react";
function Home() {
function handleSignUp(event) {
event.preventDefault()
}
return (
<form onSubmit={handleSignUp}>
<h1>Create an Account</h1>
<label htmlFor="username">Username</label>
<input type="text" id="username" />
<label htmlFor="password">Password</label>
<input type="password" id="password" />
<label htmlFor="type">Account Type</label>
<select id="type">
<option value="free">Free</option>
<option value="pro">VIP</option>
</select>
<input type="submit" value="Sign Up" />
</form>
);
}
export default Home;
Notice that we assigned ID attributes to the username and password input elements with values username and password, respectively. To retrieve it, you will need to "pull" the value of the field from the DOM, and this is usually done only once when the user hits the submit button. This type of form is called "uncontrolled" because it relies on the DOM to manage the input data.
Uncontrolled components are the easiest way to build forms if you don't need to do much validation but since we don't have control over the internal state of the input we will have to roll the input back to its previous value, which is kind of tedious to do.
Controlled Components
Form elements in HTML such as:
<input>, <select>, <textarea> and <form>
usually maintain their own states and update them according to the user input. Whereas in React, mutable states are kept in the component’s state property and can only be updated by the setState() method.
Using a controlled component is the preferred way to build your Forms in React. It lets you store the state in the component that renders the input and each input field accepts its current value as a prop and has a callback function which is called when the state of the input changes.
So here is how I created my Controlled Component in my React project:
import React, {useState} from "react";
import { useHistory } from "react-router-dom"
function PostcardForm({onAddPostcards}) {
const history = useHistory();
const [caption, setCaption] = useState("")
const [image, setImage] = useState("")
const [city, setCity] = useState ("")
const [country, setCountry] = useState("")
const [category, setCategory] = useState("")
const [trivia, setTrivia] = useState ("")
const [language, setLanguage] = useState ("")
function handleSubmit(event) {
event.preventDefault()
const formData = {
caption: caption,
image: image,
city: city,
country: country,
category: category,
trivia: trivia,
language: language
}
fetch("http://localhost:3001/postcards", {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body:
JSON.stringify(formData)
})
.then(response => response.json())
.then(finalForm => {
onAddPostcards(finalForm)
})
history.push("/postcards")
setCaption('')
setImage('')
setCity('')
setCountry('')
setCategory('')
setTrivia('')
setLanguage('')
}
return (
<div className="newPostcard">
<h4>Upload New PostCard</h4>
<form onSubmit= {handleSubmit}>
<br></br>
<label>
Caption:
<input type="text" name="caption" placeholder="Caption" value = {caption}
onChange={e => setCaption(e.target.value)} />
</label>
<br></br>
<label>
Image URL:
<input type="text" name="image" placeholder="Image URL" value = {image}
onChange={e => setImage(e.target.value)} />
</label>
<br></br>
<label>
City:
<input type="text" name="city" placeholder="City" value = {city}
onChange={e => setCity(e.target.value)} />
</label>
<br></br>
<label>
Country:
<input type="text" name="country" placeholder="Country" value = {country}
onChange={e => setCountry(e.target.value)} />
</label>
<br></br>
<label>
Category:
<input type="text" name="category" placeholder="Category" value = {category}
onChange={e => setCategory(e.target.value)} />
</label>
<br></br>
<label>
Trivia:
<input type="text" name="trivia" placeholder="Trivia" value = {trivia}
onChange={e => setTrivia(e.target.value)} />
</label>
<br></br>
<label>
Language:
<input type="text" name="language" placeholder="Language" value = {language}
onChange={e => setLanguage(e.target.value)} />
</label>
<br></br>
<button type="submit"><h3>Upload</h3></button>
</form>
</div>
);
}
export default PostcardForm;
Since the value of the input field changes whenever you type a new character, so the state changes continuously. The state does not need to be stored in same component, these can passed down as props or can be stored in a parent component.
This may sound bad but it’s actually useful, as the Form component is always “aware” of the state and the value of the input. Thus, if you want to validate something, you don’t need to “pull” the state value on submit and then display error messages.
For example, if you want to only take numbers 0 through 8, you can display a validation message in real-time, and this contributes to a better user experience.
In Conclusion
I found this helpful tip from Alex Ritzcovan
Hopefully, this will help you decide which to use in your next project.
Top comments (0)