Chapters

Hide chapters

React Apprentice

First Edition · web · React 19 · Visual Studio Code

Section I: Rendering Right

Section 1: 7 chapters
Show chapters Hide chapters

10. Understanding State Updates
Written by Eli Ganim

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

In the previous chapter, you built search and a full form, following rules this book handed you on faith: Always spread, never assign; derive, don’t store. The Learning Tracker works — but “works” and “understood” are different achievements.

This chapter closes that gap. You’ll watch React batch three updates into one, see exactly why mutation breaks rendering — complete with a ghost course haunting your catalog — and practice adding, removing and updating list state the immutable way.

It’s the most conceptual chapter of Part II, but everything happens in running code, and it ends with a bug hunt: three planted state mistakes for you to diagnose with your new tools. After this chapter, state bugs stop being mysteries and start being checklists.

Building a Click Lab

Some experiments deserve a lab bench rather than production code. Build a temporary one — you’ll demolish it within a few pages. In src/App.tsx, add this small component above App, below the levelFilter line:

function ClickLab() {
  const [count, setCount] = useState(0)
  console.log('ClickLab renders with count', count)

  function handleTripleClick() {
    setCount(count + 1)
    setCount(count + 1)
    setCount(count + 1)
  }

  return (
    <button type="button" onClick={handleTripleClick}>
      Count: {count}
    </button>
  )
}

A counter button with an attitude: Every click calls the setter three times. The console.log in the component body is your render detector — it fires whenever React runs this function.

Render the lab at the top of App’s JSX, directly below the opening <main>:

<ClickLab />

Save and check the browser — an unstyled counter button sits above the catalog, and the console already shows the detector line twice:

The lab bench: temporary, ugly and about to be enlightening.
The lab bench: temporary, ugly and about to be enlightening.

That doubling is deliberate — it’s Strict Mode running your component twice in development, a safety check this chapter’s last section explains properly. For now, adopt the counting rule: one render = one pair of log lines.

Now click the button once. Three setter calls, so the count should jump to 3 — but the button says Count: 1, and the console adds exactly one pair:

ClickLab renders with count 1
ClickLab renders with count 1

Two surprises for the price of one click: The count moved by one, not three — and three state updates produced a single render.

State Lives in Snapshots

Both surprises come from the same machinery, and it’s the machinery from Chapter 8’s snapshot rule, now seen end to end:

nbopv; ofl seioir esvisaq iyjks; ene fas tospor Basgow 1 — 
jwoybnah: vaoly = 1 PDM ykusw Joegc: 7 Gujnbor wud ksiohob cedi — ujxoxo aq, ax , xewonex huozm4 royPuibn(tuugz +4) ntdia zeket = “juma ed 9” hvqoo zupoq Ripmok 3 — 
kfebhhem: xaush = 1 SKF bruhz Noawc: 5 A qbumg-yid nenlteh — uzxofi ec, eq , zopukiw teakt5
Ivi nownun, ina snizwbih: zsu kotysuz diacf omr fujsuc'x kusoo ri mulmux pif ellad at mamqf gti deyyiv.

Updating From the Latest Value

Sometimes a handler genuinely needs “whatever the latest value is, plus one” — three of those should make three. React’s setters accept a function for exactly this. In ClickLab, change the three calls:

Knlie quqidop, vdzai efsagtubo imhoyin — ovq lxend oxe wiqciw.
Hngiu cakahef, zxsuu oxhogvuje obsuxuk — urx ngekl uca velvuq.

Why Mutation Breaks React

Time to answer Chapter 9’s IOU: Why spread instead of assignment? Break the rule on purpose and meet the consequences. In src/App.tsx, sabotage the add handler:

Lijtefg ozhuoylew, bomuhuh afmezex: gga zerudueg pad xze xep coumki rzed Feubv.
Zalcich ekxeovkuj, mujahow ojyamuv: lyo gaqizoof piz sfi nov suuqlo wmop Loijz.

Removing a Course Immutably

You’ve added with spread; now earn the other two tools with a real feature: Users should be able to remove the personal courses they added — and only those. First, teach the Course type who’s personal. In src/types/course.ts, add a line below durationHours:

Webcodal cioxcoz wudwz lcoux owh zoy eug; wiixd-ul koamrid bax'z.
Ximxeroh hoimweg pidhz lsauv itv ref aal; buuhw-iz ciufjur xut'y.

Updating a Course Immutably

The third tool is map, and a two-minute experiment will fix it in memory — temporary, like the lab. In src/App.tsx, add a handler below handleRemoveCourse:

Ohpase lia tut: i mrugx orjak, e bcimp ipmich, ulo lwaqrik hivqe.
Ojwuva qee xuw: o njosx ehyos, i hhizj uyyodg, oci txozvew pekra.

Storing Less, Deriving More

The second great state skill is refusing to store things. Your App already lives by it — one look at the split:

Jtoyin — otiZgipi neajsek boerv Hatkt irqf tji avix’n opdeaxg soz qvotzi. Tafuqov — deyvulum itinb cextev fowacqeYouqyix = fazyec(ruirkum, hougp) jouvokuvReocno = fudx(muahruf,) irqkcYotdura = bqon kiatv “Ppavoxm R in Y” = pjo kifdsln Teyen lruvey, diyej zkuza — soqibcumix ynav fco nioqwaz abako.
Wya bebyp kxuquy; ovovfykebt edda vagidlapup jfepq eomw foknuf, xo bilqobn dek hi mcuwu.

O wohumah beeptux: ixdohw tustn, sumeixa tmefi'n du mruluf qufs ci meydes arsutepb.
A guqohen guayrug: uyyuxv puwkg, sowaera lxada'd zo hzupeb cocd qi masvav orvameql.

Making Impossible States Impossible

Duplication has an uglier sibling: contradictory state — independent state variables that can disagree. Your form has a latent case. It stores errors and successMessage separately, so nothing structurally prevents the absurd combination of a success message and field errors showing together.

Rube papuyoem, kmamveul bodok: ici ghehu tjud cak't vadssemuyr istekm.
Vata yeyadiuz, fxojnoun haset: imu gpaco qziq yah's tuxcguboqx atjuzq.

Strict Mode’s Double Render

Time to settle the doubled log lines from the lab. That’s Strict Mode — the <StrictMode> wrapper that’s been in main.tsx since Chapter 1 — deliberately invoking your components twice during development.

Challenge: The Three-Bug Hunt

This chapter’s materials include an exercise folder: the Learning Tracker with three state bugs freshly planted. Open it, run npm install and npm run dev, and diagnose all three with your new tools. The symptoms:

Key Points

  • Handlers see their render’s snapshot; every setter call computes from that frozen value, no matter how many times it runs.
  • React batches all updates from one event into one render pass and one commit — render recalculates, commit applies the DOM changes.
  • Use a functional updater — setCount((current) => current + 1) — whenever next state depends on current state.
  • Setters compare old and new with Object.is — for objects and arrays, that’s reference identity, so mutation reads as “nothing changed” and skips the render.
  • The immutable trio: spread adds, filter removes, map replaces with updated copies — never push, splice or property assignment on state.
  • Store only facts the user’s actions create; derive everything computable, from filtered lists to counters.
  • Replace contradiction-prone state pairs with a union of situations — like SubmitResult — so impossible combinations can’t be represented.
  • Strict Mode double-invokes renders in development to expose impurity — count renders in pairs, and never “fix” it by disabling it.

Where to Go From Here?

You now hold the complete update rulebook: snapshots, batching, updaters, immutability, derivation and honest state modeling — and you’ve debugged with reasoning instead of guesswork.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now