Skip to main content

Controlled vs Uncontrolled Components in React


1. Introduction

What are Controlled Components?

A controlled component is a form element whose value is controlled by React state.
  • React is the single source of truth
  • Input value is driven by state (useState)
  • Changes are handled via onChange
function ControlledInput() {
  const [value, setValue] = React.useState("");

  return (
    <input
      value={value}
      onChange={(e) => setValue(e.target.value)}
    />
  );
}

What are Uncontrolled Components?

An uncontrolled component stores its own state internally in the DOM.
  • React does not control the value
  • You access values using refs
function UncontrolledInput() {
  const inputRef = React.useRef();

  const handleSubmit = () => {
    console.log(inputRef.current.value);
  };

  return (
    <>
      <input ref={inputRef} />
      <button onClick={handleSubmit}>Submit</button>
    </>
  );
}

Why is this important in React?

React is built around predictable UI via state.
  • Controlled → predictable, easier debugging
  • Uncontrolled → simpler, less code, closer to vanilla JS
Choosing incorrectly can lead to:
  • Performance issues
  • Hard-to-debug bugs
  • Poor UX

When and Why We Use It

Use Controlled Components when:

  • You need validation
  • You need to control UI dynamically
  • You want real-time updates
  • You need to sync with other components

Use Uncontrolled Components when:

  • You want quick forms
  • You don’t need real-time validation
  • You’re integrating with non-React code
  • You care about performance in large forms

2. Concepts / Internal Workings

Core Idea: Source of Truth

TypeSource of Truth
ControlledReact State
UncontrolledDOM

How Controlled Components Work Internally

  1. User types in input
  2. onChange fires
  3. React updates state
  4. Component re-renders
  5. New value flows back into input
Flow:
User Input → Event → setState → Re-render → Updated UI

How Uncontrolled Components Work Internally

  1. User types in input
  2. DOM stores value internally
  3. React is unaware of changes
  4. Value accessed via ref when needed
Flow:
User Input → DOM stores value → Access via ref

Relationship with React Features

1. useState

  • Core for controlled components
  • Enables reactivity

2. useRef

  • Core for uncontrolled components
  • Provides direct DOM access

3. Reconciliation

  • Controlled components trigger re-renders
  • Uncontrolled components bypass React updates

Synthetic Events

React uses synthetic events:
onChange={(e) => console.log(e.target.value)}
  • Wrapped browser events
  • Normalized across browsers
  • Important for controlled inputs

Default Values

  • Controlled → value
  • Uncontrolled → defaultValue
// Controlled
<input value={value} />

// Uncontrolled
<input defaultValue="Hello" />

3. Syntax & Examples


Basic Controlled Input

function App() {
  const [name, setName] = React.useState("");

  return (
    <input
      value={name}
      onChange={(e) => setName(e.target.value)}
    />
  );
}

Controlled Textarea

<textarea
  value={text}
  onChange={(e) => setText(e.target.value)}
/>

Controlled Select

<select value={option} onChange={(e) => setOption(e.target.value)}>
  <option value="A">A</option>
  <option value="B">B</option>
</select>

Controlled Checkbox

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

Multiple Inputs (Dynamic Form)

function Form() {
  const [form, setForm] = React.useState({
    name: "",
    email: "",
  });

  const handleChange = (e) => {
    setForm({
      ...form,
      [e.target.name]: e.target.value,
    });
  };

  return (
    <>
      <input name="name" value={form.name} onChange={handleChange} />
      <input name="email" value={form.email} onChange={handleChange} />
    </>
  );
}

Basic Uncontrolled Input

function App() {
  const inputRef = React.useRef();

  const handleSubmit = () => {
    alert(inputRef.current.value);
  };

  return (
    <>
      <input ref={inputRef} />
      <button onClick={handleSubmit}>Submit</button>
    </>
  );
}

Uncontrolled with Default Value

<input defaultValue="Initial value" ref={inputRef} />

<input value={value} defaultValue="Hello" />

File Input (Always Uncontrolled)

<input type="file" ref={fileRef} />
  • React cannot control file inputs
  • Always use ref

4. Edge Cases / Common Mistakes


1. Switching Between Controlled and Uncontrolled

❌ Problem:
<input value={undefined} />
  • React treats it as uncontrolled initially
  • Later becomes controlled → warning
✅ Fix:
const [value, setValue] = useState("");

2. Missing onChange in Controlled Input

❌ Problem:
<input value="Hello" />
  • Input becomes read-only
✅ Fix:
<input value={value} onChange={handleChange} />

3. Using defaultValue with Controlled Input

❌ Problem:
<input value={value} defaultValue="Hello" />
  • defaultValue is ignored

4. Performance Issues in Large Forms

Controlled inputs:
  • Re-render on every keystroke
Problem:
  • Lag in large forms
Solution:
  • Use uncontrolled inputs or debounce

5. Stale State Updates

❌ Problem:
setForm({ ...form, name: value });
  • Can cause bugs in async updates
✅ Fix:
setForm((prev) => ({ ...prev, name: value }));

6. Checkbox vs Value Confusion

❌ Problem:
<input type="checkbox" value={isChecked} />
✅ Fix:
<input type="checkbox" checked={isChecked} />

7. Ref Not Available Initially

❌ Problem:
console.log(ref.current.value); // null initially
Solution:
  • Access inside event or useEffect

5. Best Practices


1. Prefer Controlled Components (Default Choice)

  • Better predictability
  • Easier debugging
  • Centralized state

2. Use Uncontrolled for Performance-Critical Forms

  • Large forms (100+ inputs)
  • Avoid excessive re-renders

3. Use Debouncing for Controlled Inputs

const handleChange = debounce((value) => {
  setValue(value);
}, 300);

4. Keep State Minimal

❌ Bad:
const [input1, setInput1] = useState("");
const [input2, setInput2] = useState("");
✅ Better:
const [form, setForm] = useState({});

5. Avoid Direct DOM Manipulation

❌ Bad:
document.getElementById("input").value = "Hello";
✅ Use React state instead

6. Consistent Naming Conventions

  • value + onChange
  • checked + onChange
  • ref for uncontrolled

7. Use Form Libraries When Needed

For complex forms, use:
  • Formik
  • React Hook Form
They:
  • Optimize performance
  • Simplify validation
  • Reduce boilerplate

8. Validation Strategy

  • Controlled → inline validation
  • Uncontrolled → validate on submit

9. Avoid Over-Engineering

  • Don’t force controlled everywhere
  • Choose based on use-case

Summary

FeatureControlledUncontrolled
Source of truthReact stateDOM
Re-renderYesNo
ValidationEasyHarder
PerformanceCan be heavyLightweight
Use caseDynamic UISimple forms

Below is a senior-level, depth-focused question set on Controlled vs Uncontrolled Components. Each question probes internal behavior, trade-offs, and real-world decision-making, not just definitions.

1. What actually makes an input “controlled” in React beyond just using value?

Answer

A component is controlled when:
  • The rendered value is fully derived from React state
  • The DOM cannot mutate the value independently
  • Every user input flows through React before updating the UI
Key insight: It’s not just about using value — it’s about closing the loop:
<input value={state} onChange={(e) => setState(e.target.value)} />

Why this matters

React enforces a unidirectional data flow:
State → UI → User Action → Event → State Update → UI
If you break this loop (e.g., omit onChange), React still controls the value, but it becomes read-only.

Comparison

ApproachControl Level
value onlyControlled but broken (read-only)
defaultValueUncontrolled
refUncontrolled

2. How does React internally enforce control over a controlled input?

Answer

React uses a mechanism during reconciliation where:
  1. It overrides the DOM value with the value prop on every render
  2. It listens to onChange (synthetic event)
  3. It schedules state updates
  4. On re-render, it forces the DOM to match state

Important nuance

Even if the user types:
  • DOM temporarily updates
  • React immediately re-syncs it with state

Why this matters

This guarantees:
  • Deterministic UI
  • No divergence between DOM and state
But introduces:
  • Performance overhead (re-render per keystroke)

3. Why does React warn when switching between controlled and uncontrolled inputs?

Answer

Because React determines control mode on initial render.
<input value={undefined} /> // uncontrolled initially
<input value="abc" />       // becomes controlled later ❌

Internal reasoning

React sets an internal flag:
  • Controlled → expects value
  • Uncontrolled → expects DOM ownership
Switching breaks assumptions:
  • Event handling
  • Reconciliation logic
  • DOM syncing behavior

Fix

Always initialize state properly:
const [value, setValue] = useState("");

4. Why are controlled components considered more predictable?

Answer

Because:
  • State is the single source of truth
  • UI is a pure function of state
  • No hidden mutations in the DOM

Real-world impact

  • Easier debugging
  • Time-travel debugging (Redux, etc.)
  • Better testability

Comparison

AspectControlledUncontrolled
DebuggingEasyHard
State trackingExplicitImplicit

5. What are the performance implications of controlled inputs in large forms?

Answer

Each keystroke triggers:
  1. onChange
  2. State update
  3. Re-render
  4. Reconciliation

Problem

In large forms:
  • Hundreds of inputs → many re-renders
  • Can cause input lag

Example issue

<input value={value} onChange={(e) => setValue(e.target.value)} />

Solutions

  • Debouncing
  • Splitting components
  • Using uncontrolled inputs
  • Using libraries like React Hook Form

Trade-off

ControlledUncontrolled
PredictablePerformant
ExpensiveLightweight

6. Why is defaultValue ignored in controlled components?

Answer

Because:
  • Controlled components rely solely on value
  • defaultValue is only used for initial DOM state

Example

<input value="A" defaultValue="B" />
React will always render "A".

Why

React overrides DOM value every render → defaultValue becomes irrelevant.

7. Why are file inputs always uncontrolled in React?

Answer

Because browsers restrict programmatic control over file inputs for security reasons.
<input type="file" />

You cannot:

<input type="file" value="file.txt" /> // ❌ not allowed

Solution

Use refs:
const ref = useRef();
ref.current.files;

Insight

React defers to native DOM behavior where control is not safe or possible.

8. What happens if you omit onChange in a controlled input?

Answer

The input becomes effectively read-only.
<input value="Hello" />

Why

  • React controls the value
  • No way to update state → no updates

Internal behavior

React keeps forcing "Hello" on every render.

9. How do controlled components interact with React’s reconciliation?

Answer

On every state update:
  • React compares virtual DOM
  • Finds input node
  • Updates its value property

Important detail

React does not rely on DOM diffing for input values — it explicitly sets them.

Why

Input values are mutable DOM state, so React enforces consistency manually.

10. When would you intentionally choose uncontrolled components in production?

Answer

Use uncontrolled when:
  • You don’t need real-time validation
  • You want better performance
  • You integrate with non-React code (e.g., jQuery plugins)

Example

const ref = useRef();

<form onSubmit={() => console.log(ref.current.value)}>
  <input ref={ref} />
</form>

Real-world scenario

  • Large survey forms
  • Legacy integrations

11. How do controlled components enable advanced UI patterns?

Answer

Because state is centralized, you can:
  • Validate in real time
  • Format input dynamically
  • Sync multiple inputs

Example

<input
  value={value}
  onChange={(e) => setValue(e.target.value.toUpperCase())}
/>

Why uncontrolled fails here

DOM doesn’t provide hooks for transformation before rendering.

12. What are subtle bugs caused by stale state in controlled forms?

Answer

Example:
setForm({ ...form, name: value });

Problem

  • Uses outdated form reference
  • Causes lost updates in concurrent scenarios

Fix

setForm((prev) => ({ ...prev, name: value }));

Why this matters

React state updates are asynchronous and batched.

13. How does React handle cursor position in controlled inputs?

Answer

React tries to preserve cursor position, but:
  • Re-renders can reset it
  • Especially when formatting values

Example issue

onChange={(e) => setValue(format(e.target.value))}

Problem

Cursor jumps to end

Fix

  • Manage selection manually
  • Avoid aggressive formatting

14. Why is mixing controlled and uncontrolled patterns problematic?

Answer

Example:
<input value={value} defaultValue="Hello" />

Problem

  • Conflicting sources of truth
  • React ignores DOM state
  • Leads to unpredictable behavior

Rule

Pick one paradigm per input.

15. How do form libraries optimize controlled vs uncontrolled trade-offs?

Answer

Libraries like:
  • Formik → controlled-heavy
  • React Hook Form → uncontrolled-first

Key insight

React Hook Form:
  • Uses refs
  • Avoids re-renders
  • Syncs only when needed

Trade-off

LibraryStrategy
FormikControlled
RHFUncontrolled hybrid

16. How would you design a scalable form system in React?

Answer

Key decisions:
  • Use controlled for:
    • Critical inputs
    • Validation-heavy fields
  • Use uncontrolled for:
    • Bulk inputs
    • Performance-sensitive areas

Hybrid approach

  • Controlled + memoization
  • Or uncontrolled + validation on submit

17. What happens if React state lags behind user input?

Answer

UI feels unresponsive.

Cause

  • Expensive renders
  • Slow state updates

Example

onChange={(e) => {
  heavyComputation();
  setValue(e.target.value);
}}

Fix

  • Debounce
  • Move heavy logic out
  • Use useTransition (concurrent React)

18. How does uncontrolled input behavior differ in SSR (Server-Side Rendering)?

Answer

Uncontrolled inputs:
  • Use defaultValue
  • Hydration may mismatch if DOM differs
Controlled inputs:
  • Fully deterministic from state

Insight

Controlled components are safer in SSR

Summary Insight

At a senior level, this is not about what controlled/uncontrolled are — it’s about:
  • State ownership
  • Performance trade-offs
  • Predictability vs flexibility
  • Choosing the right abstraction per use-case

Below are senior-level MCQs designed to test deep understanding of Controlled vs Uncontrolled Components in React. These focus on behavior, edge cases, and decision-making, not definitions.

1. A developer writes:

<input value={undefined} onChange={(e) => setValue(e.target.value)} />
What is the actual behavior? A. Controlled input with empty value B. Uncontrolled input initially, then controlled later C. Always uncontrolled D. Throws runtime error Correct Answer: B

Explanation

  • value={undefined} makes React treat it as uncontrolled initially
  • When state updates later → becomes controlled
  • This triggers React warning: “changing uncontrolled to controlled”

Why others are wrong

  • A: Not controlled initially
  • C: It does become controlled later
  • D: No runtime error, only warning

2. What happens if you render:

<input value="Hello" />
A. Input works normally B. Input becomes uncontrolled C. Input becomes read-only D. React throws error Correct Answer: C

Explanation

  • React controls the value but no onChange → user cannot update
  • Input is effectively read-only

Why others are wrong

  • A: User cannot type
  • B: It’s controlled, not uncontrolled
  • D: No error, just warning

3. Which scenario causes the most performance overhead?

A. Uncontrolled inputs with refs B. Controlled inputs with debouncing C. Controlled inputs updating state on every keystroke D. Using defaultValue Correct Answer: C

Explanation

  • Every keystroke → state update → re-render → reconciliation
  • High cost in large forms

Why others are wrong

  • A: Minimal React involvement
  • B: Debouncing reduces overhead
  • D: No re-renders triggered

4. Consider:

<input defaultValue="A" value={state} />
What happens? A. Both values are merged B. defaultValue takes precedence C. value takes precedence D. React throws error Correct Answer: C

Explanation

  • Controlled (value) overrides everything
  • defaultValue is ignored

Why others are wrong

  • A: No merging logic exists
  • B: Only for uncontrolled
  • D: No error

5. Why does React enforce a warning when switching control modes?

A. Performance reasons B. Security reasons C. Internal consistency of input handling D. Browser limitation Correct Answer: C

Explanation

  • React tracks whether input is controlled/uncontrolled
  • Switching breaks internal assumptions

Why others are wrong

  • A: Not primary reason
  • B: Not security-related
  • D: Not browser issue

6. What happens internally when a user types in a controlled input?

A. DOM updates and React ignores it B. DOM updates, then React overwrites it C. React blocks DOM update entirely D. React bypasses reconciliation Correct Answer: B

Explanation

  • DOM updates first
  • React re-renders → enforces state value

Why others are wrong

  • A: React does not ignore
  • C: Browser still updates DOM
  • D: Reconciliation still happens

7. Which is the correct way to handle checkbox in controlled input?

A.
<input type="checkbox" value={checked} />
B.
<input type="checkbox" checked={checked} />
C.
<input type="checkbox" defaultChecked={checked} />
D. Both A and B Correct Answer: B

Explanation

  • Checkbox uses checked, not value

Why others are wrong

  • A: Incorrect property
  • C: Only for uncontrolled
  • D: A is wrong

8. Why are file inputs always uncontrolled?

A. React limitation B. Performance reasons C. Browser security restrictions D. JSX limitation Correct Answer: C

Explanation

  • Browsers prevent programmatic file setting for security

Why others are wrong

  • A: Not React limitation
  • B: Not about performance
  • D: JSX not relevant

9. What is the biggest risk of using uncontrolled inputs in large apps?

A. Performance issues B. Lack of validation control C. Memory leaks D. Slower rendering Correct Answer: B

Explanation

  • No centralized state → hard to validate or sync

Why others are wrong

  • A: Actually faster
  • C: Not typical issue
  • D: Rendering unaffected

10. Which approach minimizes re-renders in large forms?

A. Controlled inputs with state B. Controlled inputs with context C. Uncontrolled inputs with refs D. Using value without onChange Correct Answer: C

Explanation

  • No state updates → no re-renders

Why others are wrong

  • A/B: Still re-render
  • D: Broken input

11. What causes cursor jump issues in controlled inputs?

A. Missing onChange B. Using defaultValue C. Transforming value during render D. Using refs Correct Answer: C

Explanation

  • Changing value (e.g., formatting) resets cursor

Why others are wrong

  • A: Makes read-only
  • B: Not related
  • D: No cursor issue

12. Which scenario best justifies uncontrolled inputs?

A. Real-time validation B. Dynamic UI updates C. Integrating third-party DOM libraries D. Complex form dependencies Correct Answer: C

Explanation

  • Easier to integrate with non-React code

Why others are wrong

  • A/B/D: Require controlled logic

13. What is a subtle bug in this code?

setForm({ ...form, name: value });
A. Causes re-render loop B. Causes stale state issues C. Breaks controlled input D. No issue Correct Answer: B

Explanation

  • Uses stale closure → overwrites updates

Why others are wrong

  • A: No loop
  • C: Still controlled
  • D: Real issue exists

14. Why does React explicitly set input value during reconciliation?

A. To improve performance B. Because input value is mutable DOM state C. Because JSX requires it D. To avoid browser bugs Correct Answer: B

Explanation

  • Inputs have internal mutable state → React must enforce consistency

Why others are wrong

  • A: Adds overhead
  • C: Not JSX-related
  • D: Not main reason

15. What happens if ref.current.value is accessed before mount?

A. Returns empty string B. Throws error C. Returns null D. Returns undefined Correct Answer: C

Explanation

  • Ref is null before mount

Why others are wrong

  • A/D: Not initialized
  • B: No error unless accessed improperly

16. Which statement is TRUE about React Hook Form?

A. It uses controlled inputs only B. It relies heavily on refs (uncontrolled approach) C. It avoids DOM completely D. It forces re-render on every input Correct Answer: B

Explanation

  • Uses uncontrolled inputs → better performance

Why others are wrong

  • A: Incorrect
  • C: Still uses DOM
  • D: Opposite goal

17. What is the main trade-off when choosing controlled components?

A. Less flexibility B. More boilerplate and re-renders C. Reduced security D. Less browser compatibility Correct Answer: B

Explanation

  • Requires state + handlers → more renders

Why others are wrong

  • A: More flexible actually
  • C: No security issue
  • D: Fully compatible

18. In SSR, why are controlled inputs safer?

A. Faster hydration B. Less memory usage C. Deterministic state-driven rendering D. Avoid refs Correct Answer: C

Explanation

  • State ensures same output on server & client

Why others are wrong

  • A/B/D: Not core reason

Summary Insight

These questions test whether a developer truly understands:
  • Who owns the state (React vs DOM)
  • How React enforces consistency
  • Why performance trade-offs exist
  • When breaking the “controlled” rule is actually correct

Below are 20 high-quality, real-world coding problems on Controlled vs Uncontrolled Components. These are designed to test senior-level thinking, not syntax.

1. Debounced Search Input (Controlled)

Problem

Build a search input that:
  • Updates UI immediately
  • Calls API only after 300ms pause

Constraints

  • Must be controlled
  • Avoid unnecessary API calls

Expected Behavior

  • Typing “react” → API called once after user stops typing

Edge Cases

  • Rapid typing
  • Clearing input

Solution

function Search() {
  const [value, setValue] = React.useState("");

  React.useEffect(() => {
    const id = setTimeout(() => {
      if (value) console.log("API:", value);
    }, 300);

    return () => clearTimeout(id);
  }, [value]);

  return (
    <input
      value={value}
      onChange={(e) => setValue(e.target.value)}
    />
  );
}

Explanation

  • Controlled state ensures UI sync
  • Debounce prevents excessive calls

2. Large Form Optimization (Hybrid)

Problem

Create a form with 50+ inputs:
  • Avoid lag
  • Only validate on submit

Constraints

  • Minimize re-renders

Expected Behavior

  • Fast typing
  • Validation only on submit

Edge Cases

  • Missing fields
  • Partial input

Solution

function Form() {
  const refs = React.useRef({});

  const handleSubmit = () => {
    const data = Object.fromEntries(
      Object.entries(refs.current).map(([k, ref]) => [k, ref.value])
    );
    console.log(data);
  };

  return (
    <>
      {[...Array(50)].map((_, i) => (
        <input
          key={i}
          ref={(el) => (refs.current[i] = el)}
        />
      ))}
      <button onClick={handleSubmit}>Submit</button>
    </>
  );
}

Explanation

  • Uncontrolled → avoids re-renders
  • Data collected only when needed

3. Input Formatter with Cursor Preservation

Problem

Format input (e.g., credit card: 1234 5678) while typing

Constraints

  • Controlled input
  • Cursor should not jump

Expected Behavior

  • Smooth typing
  • Cursor stays correct

Edge Cases

  • Backspace
  • Middle edits

Solution (conceptual)

function format(value) {
  return value.replace(/\s/g, "").match(/.{1,4}/g)?.join(" ") || "";
}

Key Idea

  • Track cursor position
  • Restore after formatting

Explanation

  • Controlled input rewrites value → must manually handle cursor

4. Toggle Between Controlled and Uncontrolled (Safe)

Problem

Switch input mode without React warning

Constraints

  • No controlled/uncontrolled warning

Solution

const [value, setValue] = useState("");

<input
  value={isControlled ? value : undefined}
  defaultValue={!isControlled ? "Hello" : undefined}
/>

Explanation

  • Never mix modes simultaneously

5. File Upload Preview

Problem

Show image preview after file selection

Constraints

  • Must use uncontrolled input

Solution

function FileUpload() {
  const ref = useRef();
  const [preview, setPreview] = useState(null);

  const handleChange = () => {
    const file = ref.current.files[0];
    setPreview(URL.createObjectURL(file));
  };

  return (
    <>
      <input type="file" ref={ref} onChange={handleChange} />
      {preview && <img src={preview} />}
    </>
  );
}

6. Controlled Form with Dynamic Fields

Problem

Add/remove inputs dynamically

Constraints

  • Controlled state

Solution

const [fields, setFields] = useState([""]);

const update = (i, val) => {
  const copy = [...fields];
  copy[i] = val;
  setFields(copy);
};

7. Real-Time Validation Form

Problem

Validate email as user types

Expected Behavior

  • Error message appears instantly

Solution

const [email, setEmail] = useState("");

const isValid = email.includes("@");

8. Prevent Re-renders with Memo

Problem

Optimize controlled inputs in large form

Solution

const Input = React.memo(({ value, onChange }) => (
  <input value={value} onChange={onChange} />
));

9. Controlled Select with Async Options

Problem

Load options from API

Solution

<select value={val} onChange={(e) => setVal(e.target.value)}>
  {options.map(o => <option key={o}>{o}</option>)}
</select>

10. Reset Form (Controlled)

Problem

Reset all inputs
setForm({ name: "", email: "" });

11. Reset Form (Uncontrolled)

formRef.current.reset();

12. Multi-Step Form State Persistence

Problem

Preserve data across steps

Solution

  • Use controlled state at parent level

13. Checkbox Group Management

Problem

Select multiple options
const [selected, setSelected] = useState([]);

14. Textarea Auto Resize

Problem

Resize based on content

Solution

  • Controlled input + ref for height adjustment

15. Lazy Controlled Input (On Blur)

Problem

Update state only on blur
<input defaultValue="A" onBlur={(e) => setValue(e.target.value)} />

16. Form Dirty Tracking

Problem

Track if form changed
const isDirty = JSON.stringify(initial) !== JSON.stringify(current);

17. Undo/Redo Input State

Problem

Maintain history of inputs

Solution

  • Stack of states

18. Controlled Input with External Store

Problem

Sync with global store (Redux-like)

Solution

  • Controlled via props

19. Autofill Detection

Problem

Detect browser autofill

Solution

  • Use onChange + useEffect

20. Hybrid Form (Best of Both Worlds)

Problem

Use controlled + uncontrolled strategically

Solution

  • Critical fields → controlled
  • Others → refs

Final Insight

These problems test your ability to:
  • Choose correct control strategy
  • Handle performance vs predictability
  • Solve real UX issues (cursor, validation, scaling)
  • Design scalable form systems

Below are 18 production-grade debugging challenges focused on Controlled vs Uncontrolled Components. These simulate real code review scenarios with subtle issues.

1. Controlled → Uncontrolled Warning

Buggy Code

function Input({ value }) {
  return <input value={value} onChange={() => {}} />;
}

Problem

  • If value is undefined, React treats input as uncontrolled initially.

Why it happens

React determines control mode on first render. undefined = uncontrolled.

Fix

<input value={value ?? ""} onChange={() => {}} />

Best Practice

Always initialize controlled inputs with non-null values.

2. Read-Only Input Bug

Buggy Code

<input value="Hello" />

Problem

  • Input cannot be edited

Why

No onChange → React keeps forcing value

Fix

const [val, setVal] = useState("Hello");
<input value={val} onChange={(e) => setVal(e.target.value)} />

Best Practice

Never use value without onChange unless intentionally read-only.

3. Stale State in Form Update

Buggy Code

setForm({ ...form, name: e.target.value });

Problem

  • Loses updates in concurrent renders

Why

Uses stale closure of form

Fix

setForm(prev => ({ ...prev, name: e.target.value }));

Best Practice

Use functional updates for dependent state.

4. Cursor Jump Issue

Buggy Code

<input
  value={value.toUpperCase()}
  onChange={(e) => setValue(e.target.value)}
/>

Problem

  • Cursor jumps to end

Why

Value transformation triggers full re-render

Fix

onChange={(e) => {
  const val = e.target.value;
  setValue(val.toUpperCase());
}}
(Advanced: preserve cursor manually)

Best Practice

Avoid formatting inside render.

5. Mixing Controlled & Uncontrolled

Buggy Code

<input value={value} defaultValue="Hello" />

Problem

  • defaultValue ignored

Why

Controlled input overrides DOM state

Fix

<input value={value} />
OR
<input defaultValue="Hello" />

Best Practice

Never mix both patterns.

6. Checkbox Bug

Buggy Code

<input type="checkbox" value={checked} />

Problem

Checkbox doesn’t reflect state

Why

Checkbox uses checked, not value

Fix

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

Best Practice

Know input-specific props.

7. Ref Null Access

Buggy Code

const ref = useRef();
console.log(ref.current.value);

Problem

  • ref.current is null initially

Why

Ref assigned after mount

Fix

useEffect(() => {
  console.log(ref.current.value);
}, []);

Best Practice

Access refs after mount or in event handlers.

8. File Input Controlled Attempt

Buggy Code

<input type="file" value={file} />

Problem

  • Does not work

Why

Browser blocks programmatic control

Fix

<input type="file" ref={ref} />

Best Practice

File inputs must be uncontrolled.

9. Performance Lag in Large Form

Buggy Code

{fields.map((f, i) => (
  <input
    key={i}
    value={values[i]}
    onChange={(e) => update(i, e.target.value)}
  />
))}

Problem

  • Lag on typing

Why

Every keystroke → full form re-render

Fix

  • Memoize inputs
  • Or switch to uncontrolled

Best Practice

Avoid heavy controlled forms without optimization.

10. Incorrect Default Value Update

Buggy Code

<input defaultValue={props.value} />

Problem

  • Doesn’t update when props.value changes

Why

defaultValue only applies on mount

Fix

<input value={props.value} onChange={...} />

Best Practice

Use controlled inputs for dynamic updates.

11. Missing Key Causes Input Reset

Buggy Code

{items.map((item, i) => (
  <input value={item.value} />
))}

Problem

  • Inputs reset unexpectedly

Why

Missing key → React remounts elements

Fix

<input key={item.id} value={item.value} />

Best Practice

Stable keys are critical in forms.

12. Debounce Inside Render

Buggy Code

const handleChange = debounce((val) => setValue(val), 300);

Problem

  • Debounce recreated every render

Why

Function identity changes

Fix

const handleChange = useMemo(
  () => debounce((val) => setValue(val), 300),
  []
);

Best Practice

Memoize expensive functions.

13. Uncontrolled Input Not Resetting

Buggy Code

<input ref={ref} />
<button onClick={() => (ref.current.value = "")}>Reset</button>

Problem

  • Works inconsistently

Why

Direct DOM mutation bypasses React lifecycle

Fix

formRef.current.reset();

Best Practice

Use form reset API.

14. Controlled Select Mismatch

Buggy Code

<select value={value}>
  <option value={1}>One</option>
</select>

Problem

  • No option selected

Why

Value type mismatch (number vs string)

Fix

<option value="1">One</option>

Best Practice

Ensure consistent types.

15. Async State Overwrite

Buggy Code

onChange={(e) => {
  setValue(e.target.value);
  setValue(e.target.value + "!");
}}

Problem

  • First update lost

Why

State batching

Fix

setValue(prev => prev + "!");

Best Practice

Understand batching behavior.

16. Controlled Input Losing Focus

Buggy Code

<input key={Math.random()} value={value} />

Problem

  • Loses focus on every render

Why

New key → remount

Fix

<input value={value} />

Best Practice

Never use unstable keys.

17. Incorrect Ref Storage

Buggy Code

const refs = {};
<input ref={(el) => (refs[i] = el)} />

Problem

  • Refs lost on re-render

Why

Object recreated each render

Fix

const refs = useRef({});

Best Practice

Persist refs using useRef.

18. Validation Lag Bug

Buggy Code

onChange={(e) => {
  setValue(e.target.value);
  validate(value);
}}

Problem

  • Validation uses old value

Why

State updates are async

Fix

const val = e.target.value;
setValue(val);
validate(val);

Best Practice

Use latest value directly from event.

Final Insight

These bugs reveal whether a developer truly understands:
  • React’s control model (state vs DOM)
  • Reconciliation behavior
  • Event timing & batching
  • Performance implications
  • Subtle UX issues (cursor, focus, lag)

Below are 20 production-level machine coding problems centered around Controlled vs Uncontrolled Components. These reflect real frontend architecture challenges seen in top-tier interviews and real systems.

1. Google Docs–Style Auto-Save Input

Requirements

  • Text editor that:
    • Updates UI instantly
    • Auto-saves after 2s inactivity
    • Shows “Saving…” and “Saved”

UI Behavior

  • Typing is smooth
  • Status indicator updates correctly

State/Data Flow

  • Controlled input for text
  • Debounced side-effect for save

Edge Cases

  • Rapid typing
  • Network failures
  • Same content (no redundant save)

Performance

  • Avoid excessive API calls

Suggested Architecture

  • Controlled input + useEffect debounce
  • Separate status state

Approach

  1. Controlled text
  2. Debounce effect
  3. Compare previous value before saving
  4. Handle async state

2. High-Performance Survey Form (100+ Inputs)

Requirements

  • Large dynamic form
  • Instant typing (no lag)
  • Submit collects all values

UI Behavior

  • Smooth typing
  • No noticeable delay

State/Data Flow

  • Uncontrolled inputs via refs
  • Controlled only for critical fields

Edge Cases

  • Missing inputs
  • Dynamic field addition

Performance

  • Must avoid re-render per keystroke

Architecture

  • useRef map for inputs
  • Controlled validation layer

Approach

  1. Store refs in object
  2. On submit → extract values
  3. Validate batch

3. Credit Card Input with Formatting + Cursor Preservation

Requirements

  • Format: 1234 5678 9012 3456
  • Cursor should not jump

UI Behavior

  • Smooth typing anywhere in string

State/Data Flow

  • Controlled input

Edge Cases

  • Backspace in middle
  • Paste input

Performance

  • Minimal re-renders

Architecture

  • Controlled + cursor tracking

Approach

  1. Strip spaces
  2. Format
  3. Restore cursor manually

4. Multi-Step Form with Persistence

Requirements

  • Step-based form
  • Data persists across steps

UI Behavior

  • Back/Next navigation retains values

State/Data Flow

  • Central controlled state

Edge Cases

  • Partial completion
  • Refresh persistence

Performance

  • Avoid unnecessary renders

Architecture

  • Parent-managed state
  • Step components

Approach

  1. Store form in parent
  2. Pass props down
  3. Persist in localStorage

5. Search with Instant UI + Deferred Filtering

Requirements

  • Instant typing
  • Expensive filtering delayed

UI Behavior

  • No lag

State/Data Flow

  • Controlled input
  • Deferred computation

Edge Cases

  • Empty input
  • Large dataset

Performance

  • Avoid blocking main thread

Architecture

  • Controlled + useDeferredValue

6. Editable Table (Spreadsheet-like)

Requirements

  • Cells editable inline
  • Bulk updates

UI Behavior

  • Fast typing across cells

State/Data Flow

  • Hybrid (controlled + uncontrolled)

Edge Cases

  • Rapid edits
  • Focus switching

Performance

  • Avoid full table re-render

Architecture

  • Cell-level memoization
  • Local state per cell

7. Chat Input with Optimistic UI

Requirements

  • Message input
  • Instant UI update
  • Async send

State/Data Flow

  • Controlled input

Edge Cases

  • Send failure rollback

Architecture

  • Controlled + optimistic updates

8. Form Builder (Dynamic Schema)

Requirements

  • Render form from JSON schema
  • Support multiple input types

State/Data Flow

  • Dynamic controlled/uncontrolled mix

Edge Cases

  • Unknown field types
  • Validation rules

Architecture

  • Schema-driven rendering

9. File Upload with Drag & Drop + Preview

Requirements

  • Drag-drop or click upload
  • Preview files

State/Data Flow

  • Uncontrolled file input

Edge Cases

  • Multiple files
  • Invalid formats

Architecture

  • useRef + File API

10. Undo/Redo Input System

Requirements

  • Input with undo/redo

State/Data Flow

  • Controlled with history stack

Edge Cases

  • Rapid typing
  • Stack overflow

Architecture

  • Stack-based state

11. Autosuggest Dropdown (Typeahead)

Requirements

  • Suggest results as user types

State/Data Flow

  • Controlled input

Edge Cases

  • API race conditions

Architecture

  • Debounce + cancellation

12. Inline Editable Profile Form

Requirements

  • Click → edit → save

State/Data Flow

  • Controlled editing mode
  • Uncontrolled display mode

Edge Cases

  • Cancel edit

13. Form Dirty State Detection

Requirements

  • Detect if form changed

State/Data Flow

  • Controlled snapshot comparison

Edge Cases

  • Deep nested fields

14. Infinite Form (Add Rows Dynamically)

Requirements

  • Add/remove rows

State/Data Flow

  • Controlled array state

Edge Cases

  • Key stability
  • Data persistence

15. Password Strength Meter

Requirements

  • Real-time strength calculation

State/Data Flow

  • Controlled input

Edge Cases

  • Empty input
  • Complex rules

16. Controlled Input with External Store (Redux-like)

Requirements

  • Input synced with global state

State/Data Flow

  • Controlled via props

Edge Cases

  • Race conditions

17. Form with Conditional Fields

Requirements

  • Show/hide fields dynamically

State/Data Flow

  • Controlled

Edge Cases

  • Hidden field values

18. Bulk Edit Form (Edit Multiple Records)

Requirements

  • Edit multiple items at once

State/Data Flow

  • Controlled shared state

Edge Cases

  • Partial updates

19. Accessibility-Friendly Form

Requirements

  • Screen reader support
  • Keyboard navigation

State/Data Flow

  • Controlled preferred

20. Hybrid Form Optimization System

Requirements

  • Combine controlled + uncontrolled
  • Optimize large forms

Architecture

  • Critical fields → controlled
  • Others → refs

Final Architectural Insight

At senior level, these problems are not about forms — they are about:

1. State Ownership Strategy

  • Who owns data? React or DOM?

2. Performance Trade-offs

  • Controlled = predictable but expensive
  • Uncontrolled = fast but less flexible

3. UX Constraints

  • Cursor behavior
  • Real-time feedback
  • Responsiveness

4. Scalability

  • Can your form handle 10 inputs? 100? 1000?

Below are 18 FAANG-level interview questions on Controlled vs Uncontrolled Components, designed to test deep reasoning, trade-offs, and real-world decision-making.

1. How would you decide between controlled and uncontrolled inputs in a large production form?

Follow-up

  • What if the form has 200+ fields?
  • What if validation is required only on submit?

Strong Answer

A strong engineer:
  • Uses controlled inputs for:
    • Validation-heavy fields
    • Fields affecting other UI
  • Uses uncontrolled inputs for:
    • Bulk/simple inputs
  • May adopt a hybrid architecture
Reasoning: Balances predictability vs performance

Weak Answer

“Controlled is always better”
❌ Ignores performance and scalability trade-offs

2. Why do controlled inputs sometimes cause input lag?

Follow-up

  • How would you fix it without rewriting everything?

Strong Answer

  • Each keystroke → state update → re-render → reconciliation
  • Lag occurs when:
    • Component tree is large
    • Heavy computations run on render
Fixes:
  • Debounce updates
  • Split components
  • Use React.memo
  • Use uncontrolled where possible

Weak Answer

“Because React is slow”
❌ Doesn’t identify root cause

3. What happens internally when a user types into a controlled input?

Follow-up

  • Does React block DOM updates?

Strong Answer

  • DOM updates first
  • React captures event
  • State updates
  • React re-renders
  • React overwrites DOM value

Weak Answer

“React directly updates state before DOM”
❌ Incorrect order of operations

4. Why is switching between controlled and uncontrolled inputs problematic?

Follow-up

  • What bugs can it cause in production?

Strong Answer

  • React determines control mode on initial render
  • Switching breaks internal assumptions
  • Leads to:
    • Warnings
    • Inconsistent UI state

Weak Answer

“Because React doesn’t allow it”
❌ No reasoning

5. You notice cursor jumping in a formatted input. How do you debug it?

Follow-up

  • How would you fix it?

Strong Answer

  • Identify value transformation during render
  • React re-renders → resets cursor
Fix:
  • Preserve cursor manually
  • Avoid aggressive formatting

Weak Answer

“Use uncontrolled input”
❌ Avoids problem instead of solving

6. Why are file inputs always uncontrolled?

Follow-up

  • How would you design a file upload system?

Strong Answer

  • Browser security prevents programmatic value setting
  • Must use ref

Weak Answer

“React limitation”
❌ Incorrect reasoning

7. How would you design a high-performance form system at scale?

Follow-up

  • Compare Formik vs React Hook Form

Strong Answer

  • Hybrid approach:
    • Controlled for critical fields
    • Uncontrolled for bulk inputs
  • Use libraries:
    • React Hook Form (uncontrolled)
    • Formik (controlled)

Weak Answer

“Use useState everywhere”
❌ Doesn’t scale

8. Why does defaultValue not update after initial render?

Follow-up

  • When is it appropriate to use?

Strong Answer

  • Applied only on mount
  • DOM owns state afterward

Weak Answer

“It’s a bug”
❌ Misunderstanding

9. What are subtle bugs caused by stale state in controlled forms?

Follow-up

  • How does React batching affect this?

Strong Answer

  • Stale closure leads to overwritten updates
  • Use functional updates

Weak Answer

“It sometimes doesn’t update”
❌ No root cause

10. How do controlled components affect SSR and hydration?

Follow-up

  • What issues can occur with uncontrolled inputs?

Strong Answer

  • Controlled → deterministic output
  • Uncontrolled → possible hydration mismatch

Weak Answer

“No difference”
❌ Incorrect

11. When would you intentionally avoid controlled components?

Follow-up

  • Give a real production example

Strong Answer

  • Large forms
  • Third-party integrations
  • Performance-critical scenarios

Weak Answer

“Never”
❌ Dogmatic thinking

12. How does React ensure consistency between DOM and state?

Follow-up

  • Why doesn’t React rely on DOM diffing for inputs?

Strong Answer

  • React explicitly sets input value
  • Input is mutable DOM state

Weak Answer

“React just re-renders”
❌ Too shallow

13. You see an input losing focus on every keystroke. How do you debug?

Follow-up

  • What role do keys play?

Strong Answer

  • Likely remounting due to unstable keys
  • Fix key stability

Weak Answer

“React bug”
❌ No debugging mindset

14. How would you implement real-time validation without hurting performance?

Follow-up

  • What if validation is expensive?

Strong Answer

  • Controlled input
  • Debounce validation
  • Possibly move validation off main thread

Weak Answer

“Validate on every keystroke”
❌ Ignores performance

15. What are trade-offs between React Hook Form and Formik?

Follow-up

  • When would you choose each?

Strong Answer

  • React Hook Form:
    • Uncontrolled → performant
  • Formik:
    • Controlled → predictable

Weak Answer

“Both are same”
❌ No differentiation

16. How do you handle dynamic forms with hundreds of fields?

Follow-up

  • How do you avoid re-renders?

Strong Answer

  • Use uncontrolled inputs + refs
  • Lazy validation
  • Virtualization if needed

Weak Answer

“Loop and render inputs”
❌ No performance thinking

17. What happens if React state lags behind user input?

Follow-up

  • How do you prevent this?

Strong Answer

  • UI becomes unresponsive
  • Fix via:
    • Debouncing
    • Splitting components
    • Avoid heavy computation

Weak Answer

“Increase state speed”
❌ Not meaningful

18. How would you debug a form where values randomly reset?

Follow-up

  • What are the top 3 suspects?

Strong Answer

  • Key instability
  • Controlled/uncontrolled switch
  • State overwrite

Weak Answer

“Check console”
❌ No structured debugging

Final Interview Insight

A strong candidate demonstrates:

1. Decision-Making

  • Not “controlled vs uncontrolled”
  • But when and why

2. Systems Thinking

  • Forms as systems, not inputs
  • State flow, performance, UX

3. Debugging Mindset

  • Identifies root causes (not symptoms)

4. Trade-off Awareness

  • Predictability vs performance
  • Simplicity vs scalability