Differences Between HTML Form
- The
forattribute in HTML form is replaced byhtmlForin React <label htmlFor="nameId">Name:</label>
Controlled vs Uncontrolled Elements
A controlled input is one where the <input>, <select>, or<textarea> element sets the value attribute/prop. Then React expects the app to update that value via onChange
- Nothing will show up even if the user tries typing in input
- Requires app to handle onChange event to update the
value - If the input type is
file, then an uncontrolled element is required
An uncontrolled input is when the value attribute/prop is not set
// Controlled input
function Input() {
const [query, setQuery] = useState('');
function handleChange(event) {
setQuery(() => event.target.value);
}
return (
<input
type="text"
value={query} // controlled
onChange={handleChange} // needed to update state
placeholder="Search..."
/>
);
}// Uncontrolled input
function Input() {
return <input type="text" placeholder="Search..." />;
}Common elements that can be controlled
They will need an onChange event handler to update the prop
| Element | Controlled Prop | Notes |
|---|---|---|
| <input type="text" /> | value | Standard text input. |
| <input type="password" /> | value | Same as text input. |
| <input type="number" /> | value | Value is a string; convert to number if needed. |
| <input type="email" /> | value | Same as text input. |
| <input type="checkbox" /> | checked | e.target.checked gives boolean value. |
| <input type="radio" /> | checked | Usually grouped by name; e.target.value gives selected value. |
| <textarea> | value | Multi-line text input. |
| <select> | value | Single select. e.target.value gives selected option. |
| <select multiple> | value | Multi-select. Use Array.from(e.target.selectedOptions, o => o.value) to get array of selected values. |
| Custom input components | Depends on prop (value or checked) | Pass value/checked from parent and trigger handler on change. |
Uncontrolled Sample for Form
- The
<input>does not itsvalueattribute set - Use
useRefhook to get the value of the form once submit is pressed. - Use
formRef.current.reset()to reset the whole form. - Easy to understand for small forms
import React, { useRef } from 'react';
export default function UncontrolledForm() {
const nameRef = useRef(null);
const colorRef = useRef(null);
const formRef = useRef(null);
// Handle form submission
const handleSubmit = (e) => {
e.preventDefault();
const name = nameRef.current.value;
const color = colorRef.current.value;
alert(`Submitted:\nName: ${name}\nFavorite Color: ${color}`);
};
// Handle form reset
const handleReset = () => {
formRef.current.reset();
};
return (
<form ref={formRef} onSubmit={handleSubmit}>
{/* Input Text */}
<div>
<label htmlFor="name">Name:</label>
<input
id="name"
name='name'
type="text"
ref={nameRef}
placeholder="Enter your name"
/>
</div>
{/* Select Dropdown */}
<div>
<label htmlFor="color">Favorite Color:</label>
<select
id="color"
name='color'
ref={colorRef}
>
<option value=''>-- Select --</option>
<option value="Red">Red</option>
<option value="Blue">Blue</option>
<option value="Green">Green</option>
</select>
</div>
{/* Buttons */}
<div>
<button type="button" onClick={handleReset}>Reset</button>
<button type="submit">Submit</button>
</div>
</form>
);
}Controlled Sample for Form
- More scalable than uncontrolled version
- Use
useStatehook to keep track of the value of the fields. - Use
setState()to reset the whole form at once - Good for large forms
- Good for forms that need validation on certain fields
import React, { useState } from 'react';
export default function App() {
const [formData, setFormData] = useState({});
const [status, setStatus] = useState('');
const handleChange = ({ target }) => {
const { name, value } = target;
setFormData((prev) => ({
...prev,
[name]: value, // dynamic field update
}));
};
const handleSubmit = (e) => {
e.preventDefault();
setStatus('Submitting...');
alert(JSON.stringify(formData, '', 2));
};
const handleReset = () => {
setFormData({});
};
return (
<>
<form onSubmit={handleSubmit}>
{/* Input Text */}
<div>
<label htmlFor="name">Name:</label>
<input
id="name"
name="name"
type="text"
value={formData.name}
onChange={handleChange}
placeholder="Enter your name"
required
/>
</div>
{/* Select Dropdown */}
<div>
<label htmlFor="color">Favorite Color:</label>
<select
id="color"
name="color"
value={formData.color}
onChange={handleChange}
required
>
<option value="">-- Select --</option>
<option value="Red">Red</option>
<option value="Blue">Blue</option>
<option value="Green">Green</option>
</select>
</div>
{/* Buttons */}
<div>
<button type="button" onClick={handleReset}>Reset</button>
<button type="submit">Submit</button>
</div>
</form>
{status && <p>{status}</p>} // If status is true, render <p>{status}</p>
</>
);
}Submitting Form
Use the onSubmit attribute on the <form> element to give the callback function
- Use the Fetch API with POST method: Fetch API notes
- Zybooks is a good place to test form by sending the form to this link: https://wp.zybooks.com/form-viewer.php
- To test the form, set its attribute
action="https://wp.zybooks.com/form-viewer.php" - It will be possible to view what data the server sees
const handleSubmit = async (event) => {
event.preventDefault(); // prevent browser from refreshing or navigating away on submit
setStatus('Submitting...');
try {
const response = await fetch('https://wp.zybooks.com/form-viewer.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams(formData),
});
if (response.ok) {
setStatus('Form submitted successfully!');
handleReset(); // reset state
} else {
setStatus('Submission failed. Please try again.');
}
} catch (error) {
setStatus('Network error. Please try later.');
}
};