Skip to solution
mediumFrontend

Explain the concept of reconciliation in React.

1.0k views
01

Understand the problem

Question presented to candidate: "React re-renders and produces a new element tree. How does it decide what to actually change in the DOM — and why does key matter so much?"

What a strong answer should cover:

  • Reconciliation is React comparing the new element tree against the previous one and applying the minimum set of DOM operations. It is not comparing against the DOM itself.
  • A general tree diff is O(n³), so React uses two heuristics to make it O(n).
  • Heuristic 1 — different type means discard. If the element type at a position changed, React unmounts that subtree and mounts a new one. All state is lost.
  • Heuristic 2 — keys identify siblings across renders. Within a list, the key tells React that "this is the same item as before", even if its position moved.
  • Position is the default identity. Without keys, React pairs children by index — so inserting at the front makes every subsequent item look "changed".
  • The practical consequence: index keys are wrong whenever the list can reorder, insert or delete, because component state and DOM state stay bound to the position, not the item.
  • Same type and same key means React reuses the instance: state survives, only the changed props are applied.
  • Changing a component's key is the deliberate way to reset its state.

Clarifying questions expected:

  • "Can this list reorder, or have items inserted anywhere but the end?" — that decides whether index keys are acceptable.
  • "Do the rows hold their own state — inputs, toggles, animations?"

Code / implementation expected: Optional. Demonstrating a checkbox landing on the wrong row is far more convincing than describing it.

virtual domreconciliationdiffingrendering
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Engineers preparing for React interviews — assumes rendering basics. Difficulty: Medium

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:. Every behaviour below was executed against React 19.2.8, including the misas

Solution ready — 2 min read

Classified // press E to declassify

04

Explore the playground snippets

The same list with index keys and stable keys — prepend a row and watch the tick
Run Playground
import { useState } from "react";

// Each row holds its OWN state that is not derived from props. That is what
// gets misassigned when React matches children by position.
function Row({ item }) {
  const [checked, setChecked] = useState(false);
  return (
    <label style={{ display: "block", fontSize: 13, padding: "2px 0" }}>
      <input type="checkbox" checked={checked} onChange={(e) => setChecked(e.target.checked)} />
      {" "}{item.name}
      {checked && <strong style={{ color: "#4f46e5" }}> ← ticked</strong>}
    </label>
  );
}

function List({ title, items, keyBy }) {
  return (
    <div style={{ border: "1px solid #ddd", borderRadius: 8, padding: 12, flex: 1 }}>
      <strong style={{ fontSize: 13 }}>{title}</strong>
      <div style={{ marginTop: 6 }}>
        {items.map((it, i) => (
          <Row key={keyBy === "index" ? i : it.id} item={it} />
        ))}
      </div>
    </div>
  );
}

const START = [
  { id: "a", name: "Ann" },
  { id: "b", name: "Ben" },
  { id: "c", name: "Cal" },
];

export default function App() {
  const [items, setItems] = useState(START);
  const [n, setN] = useState(0);

  const prepend = () => {
    const names = ["Zoe", "Yan", "Xia"];
    setItems((list) => [{ id: "new" + n, name: names[n % 3] }, ...list]);
    setN((v) => v + 1);
  };

  return (
    <div style={{ padding: 24, fontFamily: "system-ui", lineHeight: 1.6, maxWidth: 560 }}>
      <p style={{ fontSize: 13, margin: "0 0 10px" }}>
        <strong>1.</strong> Tick <em>Ann</em> in both lists. <strong>2.</strong> Press prepend.
      </p>

      <div style={{ display: "flex", gap: 12, marginBottom: 12 }}>
        <List title="❌ key={index}" items={items} keyBy="index" />
        <List title="✅ key={item.id}" items={items} keyBy="id" />
      </div>

      <button onClick={prepend}>prepend a new row</button>{" "}
      <button onClick={() => { setItems(START); setN(0); }}>reset</button>

      <p style={{ fontSize: 13, color: "#666" }}>
        On the left the tick stays at position 0 and is now attached to a row
        the user has never seen. On the right it follows Ann down. Same data,
        same components — only the key differs. Nothing errors and nothing
        warns, which is exactly what makes this dangerous.
      </p>
    </div>
  );
}
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 37 of 119 decoded in the React.js track. One more won't hurt.

Back to track