React

Chapter 06: Forms & User Input — Controlled vs uncontrolled inputs, unified multi-field handlers, real-time validation, checkboxes, selects, and submit mechanics.

1. Controlled vs Uncontrolled Components

In standard HTML, form elements like <input>, <textarea>, and <select> maintain their own internal DOM state. In React, we prefer Controlled Components where React component state serves as the single source of truth.

💡 Real-World Analogy: Electronic Dashboard vs Paper Ballot

Think of entering input into a form:

  • Uncontrolled (Paper Ballot): The user writes on paper. The software has no idea what is written until someone walks over and reads the box on submit (using useRef()).
  • Controlled (Electronic Digital Keypad): Every keystroke immediately pulses into the central computer's memory (React state). The computer validates the keystroke, formats it, and reflects it on the digital screen in real time!
01

Controlled Component (Standard)

State drives the input value

  • Input has value={state} and onChange={handler}.
  • Enables live character count, instant validation, masking, and disabling submit buttons.
<input 
    value={name} 
    onChange={(e) => setName(e.target.value)} 
/>
02

Uncontrolled Component

DOM holds the value internally

  • Uses ref={inputRef} to pull value on submit.
  • Simpler for quick one-off file uploads (<input type="file" />).
const inputRef = useRef();
// Read on submit: inputRef.current.value

2. The Unified Multi-Field Form Handler

Instead of declaring 10 separate useState hooks for 10 form fields, professional developers combine all fields into a single form state object and use computed property names ([e.target.name]):

function RegistrationForm() {
    // 1. Single unified state object for all fields
    const [formData, setFormData] = React.useState({
        username: "anshuman_dev",
        email: "anshuman@example.com",
        role: "frontend",
        subscribeNewsletter: true
    });

    // 2. Universal change handler that handles text, select, and checkboxes
    function handleChange(e) {
        const { name, value, type, checked } = e.target;
        setFormData(prev => ({
            ...prev,
            [name]: type === "checkbox" ? checked : value
        }));
    }

    return (
        <form>
            <input name="username" value={formData.username} onChange={handleChange} />
            <input name="email" type="email" value={formData.email} onChange={handleChange} />
            <select name="role" value={formData.role} onChange={handleChange}>
                <option value="frontend">Frontend Engineer</option>
                <option value="backend">Backend Engineer</option>
            </select>
        </form>
    );
}
Live State Synchronization Preview:
// Current Form State in Memory (JSON): { "username": "anshuman_dev", "email": "anshuman@example.com", "role": "frontend", "subscribeNewsletter": true }

3. Form Submission & Real-Time Validation

Always prevent default browser page reload via e.preventDefault() and validate state before transmitting data to your backend API:

⚠️ Always Prevent Default on Form Submission

function SignUpForm() {
    const [email, setEmail] = React.useState("");
    const [error, setError] = React.useState("");

    function handleSubmit(e) {
        e.preventDefault(); // 🛑 Critical: Stops browser from reloading the whole page!

        if (!email.includes("@")) {
            setError("Please enter a valid email address.");
            return;
        }

        setError("");
        console.log("Submitting payload to server:", { email });
    }

    return (
        <form onSubmit={handleSubmit}>
            <input 
                type="email" 
                value={email} 
                onChange={(e) => { setEmail(e.target.value); setError(""); }} 
            />
            {error && <span style={{ color: "red" }}>{error}</span>}
            <button type="submit">Sign Up</button>
        </form>
    );
}
Rendered Component Preview:
Newsletter Sign Up
⚠️ Please enter a valid email address.

4. Hands-On Practice Challenges

Master controlled components, state reducers, and validation pipelines with these interactive exercises. Click "Solve Challenge" to open the code workspace!

🎯 Hands-on Coding Challenge Forms & Multi-Field

Challenge 1: Unified Form Change Reducer

Write a pure function handleFormChange(prevState, eventTarget) that accepts the current form state and an event target object (name, value, type, checked) and returns the updated immutable state.

📋 Requirements & What to Write:
  • Extract name, value, type, and checked from eventTarget.
  • If type === 'checkbox', store checked; otherwise store value.
  • Spread previous state to maintain immutability.
Expected Result: { username: "anshuman_singh", acceptTerms: true }
💡
Editor Instructions: Complete handleFormChange using computed property names [name].
formReducer.js
Console / Terminal Output
// Output will appear here when you click "Run Code"...
🎯 Hands-on Coding Challenge Validation Engine

Challenge 2: Multi-Rule Form Validator

Implement a validation validator validateRegistration(formData) that verifies: username $\ge 3$ chars, valid email format, password $\ge 8$ chars, and agreeToTerms === true. Return { isValid, errors }.

📋 Requirements & What to Write:
  • Check username length $\ge 3$.
  • Check email with RegExp format validation.
  • Check password length $\ge 8$ and agreeToTerms is checked.
  • Return isValid: boolean and dictionary of error messages.
Expected Result: { isValid: false, errors: { username: "...", email: "...", password: "...", agreeToTerms: "..." } }
💡
Editor Instructions: Check all validation rules and populate the errors dictionary.
formValidator.js
Console / Terminal Output
// Output will appear here when you click "Run Code"...
🎯 Hands-on Coding Challenge Serialization

Challenge 3: Form Data URL-Encoder / Serializer

Write a utility serializeToFormData(dataObject) that takes a controlled component form state object and converts it into a standard URL-encoded form submission string (e.g. username=anshuman&role=lead%20dev).

📋 Requirements & What to Write:
  • Iterate through keys and values in dataObject.
  • Use encodeURIComponent for safe URL encoding.
  • Join with ampersand & delimiters.
Expected Result: username=anshuman&role=lead%20dev&newsletter=true
💡
Editor Instructions: Serialize state into a URL-encoded string.
serializer.js
Console / Terminal Output
// Output will appear here when you click "Run Code"...