How to Display User Input in Another Element in React
Advertisement
🧩 Scenario
Architecture Walkthrough
The Controlled Input Cycle
A controlled input binds its value to React state. The four-step cycle is: the user types a character, onChange fires with the new value in e.target.value, the state setter is called, React re-renders with the new state, and the input renders with value={state}. The DOM never holds the value independently; React state is the single source of truth.
Setting value without an onChange handler creates a read-only input: the value is locked to the state and the user cannot change it. React warns about this in development. If the intent is a read-only display, use readOnly explicitly. If the intent is uncontrolled (let the DOM manage the value), use defaultValue instead of value.
Displaying Across Multiple Elements
Because the typed value lives in state, rendering it anywhere else is trivial: reference the same state variable. A live preview, a character counter, a second read-only input, and the form's displayed summary can all reference the same state variable and stay automatically in sync through React's re-render cycle.
Key Code Explained
// Basic: mirror input to another element
function MirrorInput() {
const [text, setText] = useState('');
return (
<div>
<input
type="text"
value={text} // controlled: value = state
onChange={(e) => setText(e.target.value)} // onChange: state = e.target.value
placeholder="Type something..."
/>
{/* Display same state in multiple places */}
<p>Preview: {text || <em>Nothing typed yet</em>}</p>
<input
type="text"
value={text}
readOnly // display-only mirror input
style={{ backgroundColor: '#f5f5f5' }}
/>
</div>
);
}
// Character counter with limit
function LimitedInput({ maxLength = 100 }: { maxLength?: number }) {
const [text, setText] = useState('');
const remaining = maxLength - text.length;
const isNearLimit = remaining <= 20;
return (
<div>
<textarea
value={text}
onChange={(e) => setText(e.target.value)}
rows={4}
placeholder="Write a bio..."
/>
<p style={{ color: isNearLimit ? 'orange' : 'inherit' }}>
{remaining} characters remaining
</p>
</div>
);
}
// Multi-field form with live preview
interface ProfileData {
firstName: string;
lastName: string;
title: string;
}
function ProfileForm() {
const [profile, setProfile] = useState<ProfileData>({
firstName: '',
lastName: '',
title: '',
});
const updateField = (field: keyof ProfileData) =>
(e: React.ChangeEvent<HTMLInputElement>) =>
setProfile((prev) => ({ ...prev, [field]: e.target.value }));
// Derived from state: always current, no extra useState needed
const fullName = `${profile.firstName} ${profile.lastName}`.trim();
return (
<div>
<div>
<input
value={profile.firstName}
onChange={updateField('firstName')}
placeholder="First name"
/>
<input
value={profile.lastName}
onChange={updateField('lastName')}
placeholder="Last name"
/>
<input
value={profile.title}
onChange={updateField('title')}
placeholder="Job title"
/>
</div>
{/* Live preview — always in sync with state */}
<div className="preview-card">
<h3>{fullName || 'Your Name'}</h3>
<p>{profile.title || 'Your Title'}</p>
</div>
</div>
);
}
// Controlled vs uncontrolled
// Controlled: React owns the value
<input value={name} onChange={(e) => setName(e.target.value)} />
// Uncontrolled: DOM owns the value, read via ref
const inputRef = useRef<HTMLInputElement>(null);
<input defaultValue="initial" ref={inputRef} />
// Read value: inputRef.current?.value (not React state)
The updateField helper in the multi-field form uses a curried function to avoid writing three separate onChange handlers. It returns a new handler function for each field that closes over the field name. This keeps the component body lean and the update logic consistent.
Tradeoffs
| Input type | Value source | Stays in sync | When to use |
|---|---|---|---|
| Controlled (value) | React state | Yes | When value is needed anywhere else in the UI |
| Uncontrolled (defaultValue + ref) | DOM | No (manual read) | Simple forms where value is only needed on submit |
What Interviewers Actually Check
- Whether you know the four-step controlled input cycle
- Whether you know that
valuewithoutonChangemakes the input read-only - Whether you can display the same state in multiple places simultaneously
- Whether you can distinguish
value(controlled) fromdefaultValue(uncontrolled) - Whether you can implement a character counter as a derived value (no extra state)
Follow-Up Questions
- How does
react-hook-formhandle controlled vs uncontrolled inputs differently from plain React, and what are the performance implications? - How would you debounce the state update so the preview only refreshes 300ms after the user stops typing, while keeping the input itself responsive?
- How would you reset all form fields to their initial values when a Reset button is clicked?
- What is the
useFormStatehook in React 19 (for Server Actions) and how does it differ from the useState-based approach? - How does
Formikdiffer fromreact-hook-formin its approach to controlled inputs?
Common Candidate Mistakes
- Setting
valuewithout anonChangehandler and being surprised that the input is unresponsive - Using
defaultValuewhen a controlled input is intended, then wondering why the display does not update - Adding a separate
useEffectto sync one state variable to another (derived state anti-pattern), when the value can just be computed during render - Using
onBlurinstead ofonChangefor updating state, so the display only updates when focus leaves the input - Using
refto read the input value and not knowing it is not in sync with React state during rendering
Interview Readiness Checklist
Before you leave this question, make sure you can answer:
- Can you implement a controlled input with
valuefrom state andonChangeupdating state? - Can you explain the four-step cycle: user types, onChange fires, state updates, React re-renders?
- Can you display the same state value in multiple elements simultaneously?
- Can you explain the difference between
value(controlled) anddefaultValue(uncontrolled)? - Can you implement a character count as a derived value without an extra
useState?
Summary
A controlled input sets its value to a React state variable and uses onChange to update that state. When the user types, onChange fires with the new value in e.target.value, the state setter is called, React re-renders, and the input displays the new state. React state is the single source of truth; the DOM reflects it.
Displaying the typed value in another element is trivial: reference the same state variable anywhere in the JSX. Live previews, character counters, formatted summaries, and mirror inputs all read from the same state and stay automatically in sync through React's render cycle.
Values derived from input state (like fullName = firstName + ' ' + lastName, or remaining = maxLength - text.length) should be computed directly during render, not stored in separate state variables updated by effects. Direct computation is always in sync, produces no extra re-renders, and requires no cleanup.
What is a controlled component?
A form element (input, select, textarea) whose value is driven by React state. The component renders the current state as the element value, and onChange updates the state. React is the single source of truth.
Advertisement
Stay Updated
Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.
Advertisement