- React
- Form Architecture, Modern FormData, and Validation
- Controlled vs Uncontrolled Inputs & Cursors
Controlled vs Uncontrolled Inputs & Cursors
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 behavior2function simulateInputCursor() {3 const logs: string[] = [];45 // 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);910 // When selectionRange is explicitly preserved using refs:11 const preservedCursor = 'cursor:preserved';12 logs.push(preservedCursor);1314 return logs.join(' ');15}1617console.log(simulateInputCursor());
Predict Console & Render Output
cursor:end cursor:preserved
cursor:preserved cursor:end
cursor:start cursor:preserved
cursor:end cursor:end
Execution Trace
Step 1 of 3 (Line 7)Async state update causes browser to jump cursor to end of string.
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:
- User types character at index 3.
onChangefires and formats value (e.g. adding dashes or uppercase).- If this state update is asynchronous or delayed, React sets the
valueproperty on the DOM node. - Setting
input.valueprogrammatically causes the browser to resetselectionStartandselectionEndto the very end of the string! - Fix: Use
useLayoutEffector synchronoussetSelectionRange(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
Why can't <input type="file" /> be a controlled input in React?
Because file inputs are read-only for security reasons; JavaScript cannot set an arbitrary file path value on the input.
What causes the "cursor jumping to the end" bug in controlled inputs?
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.
What is defaultValue used for in React?
It sets the initial value of an uncontrolled input on mount without controlling its value on subsequent renders.
Why does React warn about changing an uncontrolled input to controlled?
Because passing value={undefined} makes the input uncontrolled, but later passing value="text" makes it controlled, which leads to inconsistent state.
