By Chason ArthurRevision 132.048Published Updated

React / TypeScript / Product engineering

Build the safeguards.

Follow React Reconciliation Lab from its visible failure to a testable implementation. Each step names one decision and a checkpoint.

  1. Separate a proposal from a confirmation
  2. Make rejection a normal result
  3. Detect a competing edit
  4. Refresh every view that shares the record
  5. Give list items stable identities
  6. Measure a render before optimizing
  7. Keep failures usable from the keyboard
  8. Test the decision separately from the screen

Step 1 of 8

Separate a proposal from a confirmation

Start with a two-seat reservation. The editor can temporarily show six while the request is pending, but the confirmed record remains two. Treat pending, accepted, rejected and conflicted as distinct states. A green notification is itself application state; it must follow the response rather than the click.

const proposed = {...confirmed, seats: 6};
// proposed is not yet confirmed

Checkpoint: Before the timer finishes, identify which value is merely proposed.

Step 2 of 8

Make rejection a normal result

The fixture rejects six seats because its room limit is four. The broken implementation ignores that result and leaves six visible. The safeguarded implementation displays the confirmed record and a specific reason. In a larger editor you can preserve the rejected draft separately so the person can repair it.

if (!result.ok) {
  editor = result.record;
  message = result.reason;
}

Checkpoint: Explain why rollback and preserving a draft are separate decisions.

Step 3 of 8

Detect a competing edit

The conflict example advances the server record to version two before your version-one save arrives. The safeguarded server compares the submitted version with its current version and rejects the stale write. In production this decision must be atomic in the backend; a browser-only comparison cannot prevent competing writes.

if (requested.version !== current.version) {
  return {ok: false, record: current, reason: "Review newer version"};
}

Checkpoint: Run the conflict example and find the four-seat server record.

Step 4 of 8

Refresh every view that shares the record

The cache example accepts three seats. The broken editor updates, while its summary remains at two. The safeguard derives both from the confirmed result. A real query cache may invalidate a key or update a normalized entity; either way, enumerate the views whose meaning changed.

return {editor: result.record, summary: result.record};

Checkpoint: Name a list, badge or total that would also need refreshing in a real product.

Step 5 of 8

Give list items stable identities

React uses keys to match children across renders. Type a note beside Aster and reverse the two rows. An array-index key retains an uncontrolled input at its position, so the note appears beside the other person. A stable identifier retains the input with its record.

rows.map(person => (
  <Row key={person.id} person={person} />
))

Checkpoint: Describe the bug without using the word performance.

Step 6 of 8

Measure a render before optimizing

The render exercise updates a parent counter while child text props remain unchanged. Observe actual component invocation counts for plain and memoized children. This does not measure rendering duration or prove the whole application is faster. New object or callback props can defeat a memoization boundary.

const StableTile = memo(Tile);
// Stable primitive props can permit a render bailout.

Checkpoint: Explain why fewer renders do not automatically mean a meaningful speed improvement.

Step 7 of 8

Keep failures usable from the keyboard

The result is announced through a status region. After a rejected save, focus moves to the safeguarded error explanation. The document keeps a readable order, labels and visible focus. Test the interaction without a mouse and with enlarged text; an error should neither disappear nor trap focus.

<p role="status">Saving…</p>
<p tabIndex={-1} ref={errorRef}>Review the newer record.</p>

Checkpoint: Use Tab, Enter and Escape to run the example and open and close its source viewer.

Step 8 of 8

Test the decision separately from the screen

The pure save and reconciliation functions have regression tests. Browser checks cover the actual controls, input identity and resetting a pending timer. Keep those responsibilities distinct: a passing pure function test does not establish that the interface uses its result correctly.

const denied = saveOnServer(current, proposal, "rejected", true);
assert.equal(reconcile(current, proposal, denied, true).editor.seats, 2);

Checkpoint: Change the rejection path locally and make a test fail before repairing it.

Practice the interview explanation

  1. Where does the confirmed record live, and which views derive from it?
  2. How do you distinguish a rejected save from an unknown outcome after a network timeout?
  3. When is memoization useful, and what evidence would you collect before adding it?

Use the experiment as an example. State the failure, explain the chosen boundary, show the test and name the tradeoff. Keep measured results separate from production assumptions.

Read the primary documentation

Kindred’s full-stack role emphasizes React/Next.js, interface craft and ownership through the backend.

Related role from the job review

Choose your next step

Build the safeguards

Eight explained steps, a source example and a checkpoint at each decision.

Read the tutorial

Compare the alternatives

Understand where each pattern helps and which tradeoffs remain.

Compare approaches

Run and extend it locally

Download the independent environment, run the native checks and try a specific extension.

Open the how-to guide

Lab discussion / React Reconciliation Lab

What would you try next?

Ask about the code, compare an approach, or describe an edge case. Reader comments are reviewed before publication.

All lab discussions

Reader comments

Open the discussion to read and add reader comments.

Read or write a comment →