Skip to the solution
MediumFrontend7 min read

Explain the concept of reconciliation in React.

1.0k views

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 dom
  • reconciliation
  • diffing
  • rendering

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

7 min read Β· 9 sections

Try it in the playground

Open any example in the playground to run and change it.

ReactThe same list with index keys and stable keys β€” prepend a row and watch the tick
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>
  );
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 37 of 119 in React.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All React.js questions
Up nextMedium

Describe the React component lifecycle.

Continue