Welcome back to the React Mastery Series!
In the previous article, we explored Rendering Lists in React and learned how to dynamically generate UI using arrays, map(), and the importance of the key prop.
Today, we will explore one of the most important concepts in frontend development:
Forms in React
Forms are everywhere in modern applications:
- Login and registration
- Search functionality
- Payment processing
- Profile updates
- Banking transactions
- Checkout flows
- User management systems
A frontend developer spends a significant amount of time building and managing forms.
React provides a powerful approach to handle forms using Controlled Components
How HTML Forms Work Traditionally
In traditional HTML applications, the browser manages form values.
Example:
<form>
<input type="text" />
<button>
Submit
</button>
</form>
The browser stores the input value internally.
JavaScript retrieves the value when needed.
How React Handles Forms
React follows a different approach.
React keeps form data inside the component state.
The flow becomes:
User Types
|
↓
onChange Event
|
↓
Update React State
|
↓
State Becomes Source of Truth
|
↓
UI Updates
This pattern is called Controlled Component
What is a Controlled Component?
A controlled component is a form element whose value is controlled by React state.
Example:
import { useState } from "react";
function LoginForm() {
const [email, setEmail] = useState("");
return (
<input
type="email"
value={email}
onChange={(event) =>
setEmail(event.target.value)
}
/>
);
}
Here:
Input Value
|
↓
React State
|
↓
Component Controls Input
React becomes the single source of truth.
Understanding value and onChange
A controlled input requires two important properties (i.e value & onChange)
value
Defines what is displayed.
value={email}
onChange
Updates the state whenever the user types.
onChange={(event) =>
setEmail(event.target.value)
}
Together:
<input
value={email}
onChange={handleChange}
/>
create a controlled input.
Handling Multiple Form Fields
Real applications usually contain multiple fields.
Example:
const [formData, setFormData] = useState({
username: "",
email: "",
password: ""
});
Instead of creating separate states:
const [username,setUsername] = useState("");
const [email,setEmail] = useState("");
const [password,setPassword] = useState("");
we can manage related values together.
Updating Multiple Inputs
Example:
function RegisterForm(){
const [formData,setFormData] =
useState({
username:"",
email:"",
password:""
});
function handleChange(event){
setFormData({
...formData,
[event.target.name]:
event.target.value
});
}
return (
<form>
<input
name="username"
value={formData.username}
onChange={handleChange}
/>
<input
name="email"
value={formData.email}
onChange={handleChange}
/>
<input
name="password"
value={formData.password}
onChange={handleChange}
/>
</form>
);
}
Understanding Computed Property Names
This syntax:
[event.target.name]: event.target.value
is a JavaScript feature called Computed Property Name
Example:
const field = "email";
const user = {
[field]: "test@example.com"
};
Result:
{
email:"test@example.com"
}
React forms use this pattern frequently.
Handling Form Submission
A form submission normally refreshes the browser page.
React applications prevent this behavior.
Example:
function LoginForm(){
function handleSubmit(event){
event.preventDefault();
console.log("Form Submitted");
}
return (
<form onSubmit={handleSubmit}>
<button>Login</button>
</form>
);
}
Why use preventDefault()?
Without:
event.preventDefault();
The browser performs a full page refresh.
With React:
Submit Form
|
↓
Prevent Browser Refresh
|
↓
Validate Data
|
↓
Call API
|
↓
Update UI
Form Validation
Validation ensures users enter correct information.
Common validations:
- Required fields
- Email format
- Password rules
- Minimum length
- Business rules
Example:
if(email === ""){
setError("Email is required");
}
Showing Validation Messages
Example:
{
error &&
<p>
{error}
</p>
}
This uses conditional rendering that we learned earlier.
Complete Login Form Example
import { useState } from "react";
function LoginForm(){
const [form,setForm] =
useState({
email:"",
password:""
});
function handleChange(event){
setForm({
...form,
[event.target.name]:
event.target.value
});
}
function handleSubmit(event){
event.preventDefault();
console.log(form);
}
return (
<form onSubmit={handleSubmit}>
<input
name="email"
type="email"
value={form.email}
onChange={handleChange}
/>
<input
name="password"
type="password"
value={form.password}
onChange={handleChange}
/>
<button>
Login
</button>
</form>
);
}
export default LoginForm;
Handling Checkbox Inputs
Checkboxes work slightly differently.
Example:
const [accepted,setAccepted] = useState(false);
Component:
<input
type="checkbox"
checked={accepted}
onChange={(event)=>
setAccepted(event.target.checked)
}
/>
Use:
event.target.checked
instead of:
event.target.value
Handling Select Dropdowns
Example:
const [country,setCountry] = useState("");
Component:
<select
value={country}
onChange={(event)=>
setCountry(event.target.value)
}
>
<option>
India
</option>
<option>
UAE
</option>
</select>
Controlled vs Uncontrolled Components
React supports two approaches.
Controlled Component
React manages the value.
<input
value={name}
onChange={handleChange}
/>
Advantages:
- Easy validation
- Dynamic UI updates
- Predictable behavior
- Recommended approach
Uncontrolled Component
The DOM manages the value.
Example:
const inputRef = useRef();
<input ref={inputRef}/>
Value is accessed directly:
inputRef.current.value
Useful for:
- Simple forms
- File uploads
- Integrating with non-React libraries
Forms in Enterprise Applications
Consider a banking application:
Fund Transfer Form
Fields:
From Account
To Account
Amount
Transfer Date
Remarks
React flow:
User Enters Data
|
↓
Update Form State
|
↓
Validate Input
|
↓
Submit Request
|
↓
Call Transfer API
|
↓
Show Success/Error Message
This exact pattern is used in enterprise applications.
Common Form Mistakes
1. Not Providing value
Incorrect:
<input
onChange={handleChange}
/>
The input becomes uncontrolled.
2. Forgetting name Attribute
Incorrect:
<input />
When using a generic handler:
event.target.name
the name attribute is required.
3. Mutating Form State Directly
Incorrect:
form.email = "test@gmail.com";
Correct:
setForm({
...form,
email:"test@gmail.com"
});
4. Too Much Form Logic Inside Component
Large enterprise forms can become difficult to maintain.
Solutions:
- Custom hooks
- Form libraries
- Schema validation
Popular libraries:
- React Hook Form
- Formik
- Zod
Best Practices
- Use controlled components for most forms.
- Keep form state organized.
- Validate before submitting.
- Show meaningful error messages.
- Separate validation logic from UI.
- Reuse common form components.
Key Takeaways
Today, we learned:
✅ React forms are commonly managed using controlled components.
✅ State becomes the single source of truth for form values.
✅ value and onChange work together to control inputs.
✅ preventDefault() prevents browser refresh on submission.
✅ Multiple inputs can be managed using a single state object.
✅ Form validation is an essential part of production applications.
Coming Next 🚀
In Day 13, we will explore:
React Hooks Deep Dive – Understanding useEffect
We will learn:
- Why useEffect exists
- Component lifecycle with useEffect
- Dependency arrays
- API calls
- Cleanup functions
- Common mistakes
- Real-world examples
useEffect is one of the most important Hooks in React and is heavily used in production applications.
Happy Coding! 🚀
Top comments (0)