React Curriculum

Controlled vs Uncontrolled Inputs & Cursors

Easy100% Free~20 mins#react#forms#controlled-inputs#uncontrolled-inputs#cursor-jump#defaultvalue
Interview Answer (30-Second Summary)

Controlled inputs have their value driven by React state via the value and onChange props. Uncontrolled inputs store form data in the native DOM itself, accessed via defaultValue and refs. Formatting controlled inputs asynchronously can trigger the notorious input cursor jump bug.

Key Learning Objectives
✓

Compare controlled inputs (single source of truth in state) vs uncontrolled inputs (native DOM state).

✓

Diagnose the input cursor jump bug when reformatting controlled text inputs.

✓

Explain when defaultValue is required instead of value.

✓

Choose the appropriate pattern based on form complexity and performance requirements.

The Interview Problem & Context

What is logged to the console when contrasting an asynchronous reformat that causes an input cursor jump against synchronous cursor preservation?

1// Simulating controlled input cursor behavior
2function simulateInputCursor() {
3 const logs: string[] = [];
4
5 // When value is updated asynchronously in controlled inputs,
6 // the browser resets selectionStart/selectionEnd to the end of the input string!
7 const asyncReformatCursor = 'cursor:end';
8 logs.push(asyncReformatCursor);
9
10 // When selectionRange is explicitly preserved using refs:
11 const preservedCursor = 'cursor:preserved';
12 logs.push(preservedCursor);
13
14 return logs.join(' ');
15}
16
17console.log(simulateInputCursor());
Predict Console & Render Output
A

cursor:end cursor:preserved

B

cursor:preserved cursor:end

C

cursor:start cursor:preserved

D

cursor:end cursor:end

Execution Trace
Step 1 of 3 (Line 7)

Async state update causes browser to jump cursor to end of string.

Component / Call Stack
simulateInputCursor()
Hook State & Scope
logs[0]:"cursor:end"
Console Output(No console logs emitted)
Deep Architectural Explanation

Controlled vs Uncontrolled Inputs

Controlled Inputs

  • Mechanism: Driven by React state: <input value={text} onChange={e => setText(e.target.value)} />.
  • Pros: Instant validation, disabled submit buttons, formatting, dynamic masking.
  • Cons: Re-renders on every keystroke; can cause input lag on low-end devices.

The Input Cursor Jump Bug

If a user edits text in the middle of a controlled input:

  1. User types character at index 3.
  2. onChange fires and formats value (e.g. adding dashes or uppercase).
  3. If this state update is asynchronous or delayed, React sets the value property on the DOM node.
  4. Setting input.value programmatically causes the browser to reset selectionStart and selectionEnd to the very end of the string!
  5. Fix: Use useLayoutEffect or synchronous setSelectionRange(cursor, cursor) to restore the caret position immediately.

Uncontrolled Inputs

  • Mechanism: Driven by the DOM: <input defaultValue="Alice" ref={inputRef} />.
  • Pros: Zero re-renders during typing; excellent performance for simple forms.
  • Cons: Cannot easily disable buttons based on real-time field contents.
Common Interview Traps & Pitfalls
✕

Switching an input from uncontrolled to controlled (e.g. passing value={data} where data is undefined initially, then becomes a string).

✕

Passing value without onChange and forgetting readOnly, triggering React console warnings.

✕

Using controlled inputs for 50-field forms without memoization, causing keystroke lag.

Senior Interviewer Follow-Up Probes
?

Why does React warn "A component is changing an uncontrolled input to be controlled"?

?

How does modern FormData submission make uncontrolled inputs the preferred choice for simple forms?

?

How do you handle file upload inputs (<input type="file" />) which are always uncontrolled in React?

Frequently Asked Interview Questions
What is the main difference between controlled and uncontrolled inputs?

Controlled inputs store their data in React state and update via onChange. Uncontrolled inputs store their value in the DOM and are read using refs.

Because file inputs are read-only for security reasons; JavaScript cannot set an arbitrary file path value on the input.

Programmatically reassigning the value property of a DOM input element causes the browser to reset the text selection caret to the end of the text.

It sets the initial value of an uncontrolled input on mount without controlling its value on subsequent renders.

Because passing value={undefined} makes the input uncontrolled, but later passing value="text" makes it controlled, which leads to inconsistent state.