What bugs appear when state and props responsibilities are mixed?

HighIntermediateReact
Preparing for interviews?

Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.

Quick Answer

Explain what typically breaks when a component treats the same piece of data as both “props-owned” and “state-owned” (multiple sources of truth). Cover common symptoms (stale UI, input jumps, infinite loops, memo bugs) and the correct patterns (single source of truth, controlled vs uncontrolled, derived state). Confusing state vs props leads to bugs and re-render issues. Test with prop changes and derived state updates.

Answer

Core idea

Props are inputs owned by the parent. State is local memory owned by the component. When the same value is “owned” by both (prop + local state trying to represent the same truth), you create two sources of truth. React can only re-render correctly if ownership is clear: one writer, one authoritative value.

Bug class

What you see

Why it happens

Stale UI / missed updates

Parent updates prop, child UI still shows old value

Child copied prop into state once (or with a wrong sync) so it stops reflecting prop changes

Input “jumps” / cursor issues

User types, then text resets or cursor jumps

Both parent and child write to the input value; updates race and overwrite each other

Infinite update loops

“Maximum update depth exceeded” or render thrashing

Effect syncs prop → state, state change triggers parent callback, parent changes prop again

Controlled/uncontrolled warnings

React warning about switching controlled ↔ uncontrolled

Sometimes you pass value, sometimes you don’t; or you mix value + defaultValue

Memo / shallow-compare lies

React.memo/PureComponent skips re-render when it shouldn’t (or vice versa)

Mutations or identity mismatches: prop reference stays same while internal state changes (or you mutate a prop object)

Hard-to-debug coupling

“Works sometimes”, order/timing dependent behavior

Two writers + async scheduling = last write wins, but it’s not obvious who wrote last

Typical failures when prop + state compete for the same responsibility
JSX
// ❌ Classic bug: copying props to state (stale after parent updates)
function ProfileEditor({ user }) {
  const [name, setName] = React.useState(user.name); // copied once

  // Later: parent changes user.name (e.g., refetch) but input stays stale
  return <input value={name} onChange={(e) => setName(e.target.value)} />;
}

// This component now has two truths:
// - parent truth: user.name
// - child truth: name
                  
JSX
// ❌ Sync effect + callback can create loops / jitter
function Child({ value, onChange }) {
  const [local, setLocal] = React.useState(value);

  // tries to "keep in sync" with prop
  React.useEffect(() => {
    setLocal(value);
  }, [value]);

  // tries to "keep parent in sync" with local
  React.useEffect(() => {
    onChange(local);
  }, [local, onChange]);

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

Correct pattern

When to use

How it prevents bugs

Single source of truth (controlled)

Parent owns the value; child just renders + emits events

One writer (parent). No local copy, no drift

Uncontrolled input + ref

You don’t need every keystroke in React state

DOM owns value; React reads it only when needed (submit, blur)

Derived data (no state)

Value can be computed from props/state

No syncing needed: compute during render (optionally memoize)

Intentional “draft state” with explicit reset

You need editable local draft that can be reset by parent

You define a reset rule (key change / reset token) instead of ambiguous syncing

Fix = make ownership explicit (pick one owner per piece of data)
JSX
// ✅ Controlled: parent owns value (single source of truth)
function Child({ value, onChange }) {
  return <input value={value} onChange={(e) => onChange(e.target.value)} />;
}

function Parent() {
  const [value, setValue] = React.useState('');
  return <Child value={value} onChange={setValue} />;
}

// ✅ Uncontrolled: DOM owns value
function UncontrolledForm() {
  const ref = React.useRef(null);
  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        alert(ref.current.value);
      }}
    >
      <input ref={ref} defaultValue="" />
      <button>Submit</button>
    </form>
  );
}

// ✅ Intentional draft with explicit reset signal
function Editor({ initialName, resetKey }) {
  const [draft, setDraft] = React.useState(initialName);

  // explicit reset (not "sync every time")
  React.useEffect(() => {
    setDraft(initialName);
  }, [resetKey]);

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

Rule of thumb

If two different places can write the “same” value, you will eventually get drift, overwrites, loops, or UI jumps. Decide who owns it (parent via props, or child via local state/DOM), then design the API around that ownership.

Practical scenario
A child component both receives user data as props and stores local edits, creating conflicts.

Common pitfalls

      • Duplicating source of truth in props and state.
      • Out-of-sync updates when props change.
      • Unnecessary re-renders from derived state.
Trade-off or test tip
Keep a single source of truth. Test by changing props and verifying UI updates correctly.

Similar questions
Guides
4 / 41