How to Display User Input in Another Element in React

Beginner8 min interview
Skills tested:
Implementing a controlled input where value is set from state and onChange updates stateUnderstanding the render cycle of a controlled input: type triggers onChange, onChange updates state, state update triggers re-render, new value renderedDisplaying state in multiple places simultaneously from a single source of truthUnderstanding the difference between controlled (value + onChange) and uncontrolled (defaultValue + ref) inputsHandling a textarea as a controlled component

Advertisement

🧩 Scenario

Displaying user input in real time is a prerequisite for live previews, character count displays, search-as-you-type, and form validation. It is also the foundational example that explains React controlled components, which appear in almost every React interview covering forms.

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 typeValue sourceStays in syncWhen to use
Controlled (value)React stateYesWhen value is needed anywhere else in the UI
Uncontrolled (defaultValue + ref)DOMNo (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 value without onChange makes the input read-only
  • Whether you can display the same state in multiple places simultaneously
  • Whether you can distinguish value (controlled) from defaultValue (uncontrolled)
  • Whether you can implement a character counter as a derived value (no extra state)

Follow-Up Questions

  1. How does react-hook-form handle controlled vs uncontrolled inputs differently from plain React, and what are the performance implications?
  2. How would you debounce the state update so the preview only refreshes 300ms after the user stops typing, while keeping the input itself responsive?
  3. How would you reset all form fields to their initial values when a Reset button is clicked?
  4. What is the useFormState hook in React 19 (for Server Actions) and how does it differ from the useState-based approach?
  5. How does Formik differ from react-hook-form in its approach to controlled inputs?

Common Candidate Mistakes

  • Setting value without an onChange handler and being surprised that the input is unresponsive
  • Using defaultValue when a controlled input is intended, then wondering why the display does not update
  • Adding a separate useEffect to sync one state variable to another (derived state anti-pattern), when the value can just be computed during render
  • Using onBlur instead of onChange for updating state, so the display only updates when focus leaves the input
  • Using ref to 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 value from state and onChange updating 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) and defaultValue (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.

Related Videos
Watch these videos to learn more about this topic
Frequently Asked Questions

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