Topics in this subject
React 5 min read Updated 6 Aug 2026

Forms

Controlled vs uncontrolled inputs, multiple fields, checkboxes/selects, submission, and when to reach for RHF.

🧑‍🏫 Sabse pehle — simple mein samjho#

Controlled input matlab React state hi boss hai — asli data state mein rehta, input toh bas usko dikhata hai. value={text} state se aata hai, aur onChange har keystroke pe state update karta hai. Jaise tumhari diary tumhare haath mein — pen (input) sirf likhne ka zariya, likha hua diary (state) mein safe. Single source of truth = ek hi jagah sach.

function Name() {
  const [text, setText] = useState("");
  return (
    <input
      value={text}                                 // state dikhata hai
      onChange={e => setText(e.target.value)}       // typing → state update
    />
  );
}

Yaad rakho: input sirf dikhata hai, asli data hamesha state mein rehta hai.

Controlled inputs 🎯#

State is the single source of truth: the input's value comes from state and every keystroke flows through onChange. React re-renders on each change, so the value in state and on screen are always in sync.

function Name() {
  const [name, setName] = useState("");
  return <input value={name} onChange={e => setName(e.target.value)} />;
}

Uncontrolled inputs#

The DOM holds the value; you read it on demand via a ref and set an initial value with defaultValue (never value). No re-render per keystroke.

function Name() {
  const inputRef = useRef(null);
  function onSubmit(e) {
    e.preventDefault();
    console.log(inputRef.current.value);   // read from the DOM
  }
  return (
    <form onSubmit={onSubmit}>
      <input defaultValue="Ada" ref={inputRef} />
    </form>
  );
}

Controlled vs uncontrolled#

Aspect Controlled Uncontrolled
Source of truth React state The DOM
Value prop value defaultValue
Read value From state ref.current.value
Re-render per keystroke Yes No
Instant validation / formatting Easy 🟢 Awkward
Best for Most forms, dynamic UI File inputs, quick/simple forms, integrating non-React widgets

⚠️ Don't switch a field between controlled and uncontrolled (e.g. value={x} where x starts undefined then becomes a string) — React warns and resets cursor/behavior. Initialize state to "".

Multiple inputs with one handler#

Give each input a name and key state by it. 🟢

function Profile() {
  const [form, setForm] = useState({ email: "", city: "" });
  const onChange = e => {
    const { name, value } = e.target;
    setForm(f => ({ ...f, [name]: value }));   // computed key
  };
  return (
    <>
      <input name="email" value={form.email} onChange={onChange} />
      <input name="city"  value={form.city}  onChange={onChange} />
    </>
  );
}

Real-world example: signup form (Flipkart/IRCTC style)#

A realistic account-creation form has several fields — name, email, password — all wired to a single handleChange, plus basic validation before submitting.

function SignupForm() {
  const [form, setForm] = useState({ name: "", email: "", password: "" });
  const [errors, setErrors] = useState({});

  function handleChange(e) {
    const { name, value } = e.target;
    setForm(prev => ({ ...prev, [name]: value }));   // one handler, every field
  }

  function handleSubmit(e) {
    e.preventDefault();
    const nextErrors = {};
    if (!form.name.trim()) nextErrors.name = "Name is required";
    if (!/^\S+@\S+\.\S+$/.test(form.email)) nextErrors.email = "Enter a valid email";
    if (form.password.length < 8) nextErrors.password = "Min 8 characters";

    setErrors(nextErrors);
    if (Object.keys(nextErrors).length === 0) {
      createAccount(form);   // only submit when clean
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" value={form.name} onChange={handleChange} placeholder="Full name" />
      {errors.name && <p className="error">{errors.name}</p>}

      <input name="email" value={form.email} onChange={handleChange} placeholder="Email" />
      {errors.email && <p className="error">{errors.email}</p>}

      <input name="password" type="password" value={form.password} onChange={handleChange} placeholder="Password" />
      {errors.password && <p className="error">{errors.password}</p>}

      <button type="submit">Create account</button>
    </form>
  );
}

🟢 One handleChange covers every field because name on each <input> tells it which key of form to update — the same computed-key pattern from the section above, scaled to a real signup screen.

The controlled-input render loop#

flowchart LR
  S["State: form.email"] --> V["value prop on the input"]
  V --> IN["Input renders with that value"]
  IN --> T["User types a character"]
  T --> OC["onChange fires with e.target.value"]
  OC --> SS["setForm updates state"]
  SS --> RR["Component re-renders"]
  RR --> S

Checkboxes & selects#

Checkboxes use checked/e.target.checked (not value). A <select multiple> yields multiple selected options.

<input type="checkbox" checked={agree} onChange={e => setAgree(e.target.checked)} />

<select value={city} onChange={e => setCity(e.target.value)}>
  <option value="del">Delhi</option>
  <option value="blr">Bengaluru</option>
</select>

Submission#

Handle onSubmit on the <form> (fires on Enter and button click) and call e.preventDefault() to stop the full-page reload.

<form onSubmit={e => { e.preventDefault(); save(form); }}>…<button>Save</button></form>

When to reach for a library#

For large forms, complex validation, and performance, React Hook Form uses uncontrolled inputs + refs to minimize re-renders (register, handleSubmit), pairing well with schema validators like Zod/Yup. Formik is the older controlled-state alternative.

flowchart TD
  A["User types"] --> B{"Controlled?"}
  B -->|Yes| C["onChange -> setState"] --> D["Re-render, value = state"]
  B -->|No| E["DOM stores value"] --> F["Read via ref on submit"]

Interview Q&A#

Q1. Controlled vs uncontrolled — the one-line difference? Controlled inputs derive their value from React state (value + onChange); uncontrolled inputs keep the value in the DOM and you read it via a ref (defaultValue).

Q2. When would you prefer uncontrolled? File inputs (which are always uncontrolled), simple forms where per-keystroke state is unnecessary, integrating third-party/non-React widgets, and to avoid re-renders in very large forms.

Q3. Why does React warn about switching controlled/uncontrolled? Passing value={undefined} then a string makes React unsure who owns the value, causing lost input state and cursor jumps. Initialize state to a defined value like "".

Q4. How do you handle many inputs cleanly? Give each a name, store an object in state, and use a computed key: setForm(f => ({ ...f, [name]: value })).

Q5. Why is React Hook Form fast? It keeps inputs uncontrolled and subscribes via refs, so typing doesn't trigger component re-renders — only subscribed fields update.