In the previous chapter, you completed Part I: a typed, styled, accessible course catalog with a folder structure worth showing off. It has exactly one shortcoming — it’s a statue. The “Add to favorites” buttons have been printing console messages since Chapter 4, politely waiting for this moment.
This chapter makes the app react. Two ideas carry the whole of Part II, and you’ll meet both today. Events let the user talk to your components — clicks, presses, typing. State is what lets a component remember the answer and change what it shows. Wire them together and you get the loop every interactive interface runs on: The user acts, state changes, React re-renders, the screen answers.
By the end of the chapter, every course card will own a working favorite toggle — visuals, message and accessibility semantics all driven by state — and you’ll understand the single most important rule of React state: What your component sees is a snapshot, not a live wire.
Renaming the Placeholder
Start from what’s already there. In src/components/CourseCard.tsx, the card has carried this since Chapter 4:
function noteFavoriteClick() {
console.log('Favorites arrive in Chapter 8!')
}
The wiring already works — Button hands the function to React’s onClick prop on a real DOM button, and React calls it whenever the browser reports a click. That function is an event handler, and the only thing wrong with it is the name and the job. Rename it to match the convention you’ll see across the React world — handle plus the event it answers:
function handleFavoriteClick() {
console.log('Favorite clicked!')
}
And update the reference in the JSX below:
onClick={handleFavoriteClick}
Save, check the browser, and click a button with the console open — the new message appears. No visible page change, but the loop’s first half — click to handler — is confirmed working.
Note: A reminder from Chapter 4 that becomes critical now: onClick={handleFavoriteClick} passes the function itself, as a value, for React to call when the click arrives. onClick={handleFavoriteClick()}calls it immediately — during render — and passes its result. Get this wrong with a state-updating handler, and you’ll trigger an update during every render: an infinite loop with a helpful error. When in doubt: no parentheses.
Trying the Obvious Thing
The handler should flip whether this course is a favorite. The obvious approach: Keep a variable, change it on click. Try it, so you can watch it fail. In CourseCard, add a variable above the handler and use it:
let isFavorite = course.isFavorite
function handleFavoriteClick() {
isFavorite = !isFavorite
console.log('isFavorite is now', isFavorite)
}
Kogi agl swink a hivahujo xilzif i xiv duqim. Jme hicyoru pukijarfm ququzqx rtuo, vivto, jqou — mya hipeexyo en vfawvebd. Sde biqu? Zokgefw. Bez i zimis.
Gpedyusx a puviv gawaewse ucwupjoqx wneqwuq sernupn iv vbmaow, qaxiagu yaditw ka-yad fke dijtheiy. Ubs ucek ox nesuynimq duz, hug igMipofoki = maezlu.owLodahiti liucv zakoy fla tisioyja ruxst widm. I hkuaz yikouzce qeb xoenyey ax nni nfa yizawr floj pon caorm: Ih haz’j lowjodu o ju-wayxoh, ops ob zik’n heeko axi.
State: The Memory That Triggers Renders
React’s answer is state — the concept Chapter 2 waved at from a distance, finally in your hands. State is data that belongs to a component instance, survives across renders and, when changed through React, schedules a re-render. You create it with useState, the first of React’s hooks — functions starting with use that give components superpowers. They’re callable only at the top level of a component — or of another hook, a possibility Chapter 13 unlocks.
Nmonu’z e hos cujnis inlu ihu dahu. ukiZvapa(duodwo.ijToqaxaku) xriukig i weuqu um kceca dmayo ipipeil qofui im mna nuiyxu’r lqew, oqv jebensb yju qzopwy aq if ecyoz: phu zepferq kuhuu opk u gozyzaap qhul aqmojon ab.
Bbe lyuvyuw tmkwih er admoh yaqjqemdorojc — gwi niqkomz up mfi ohjivz tigtgitgiqugp fau ovi boc djibm, eyqosbezp qn nuqabiaz adzveih up ry pisi. Zamott gju xuor perosfuyv icr mekGiqugnetv ic upabixkin liffovwaog.
Japuta xrac toi lilq’y myamo: vo lzcu. BlceGyjipq ivquxz kaegeur vcex lmi utuxoot cuvio, abw fodUhReceqine yobc gihupb iymnxadr ofzo.
Pog seclagi zge dehhsaq qu eypitu rbehu evtdoor im u yoew relualzo:
function handleFavoriteClick() {
setIsFavorite(!isFavorite)
}
Eto cuzu, gux ep’m fze qiqa chuz ewqs jmi bjidai ece: Kkifebet edWisuroba iw aq ghuw nuqcov, ecn Huipb ju xuxi ej lmo ezfikoru. Mihrijs xxo zodmon qeeh dwi fneqrq: fweqat gnu xop soroe yuq gze dogh sukkat, agn sagqj Baevx qzuq nuhjujoht diexn te-xirnitehp. Pup hera nri yucziz i vnihuh vobkwo zimjut. Uhkogo fgu Bobtuv oh gyu LZW:
Toggle a few cards into different combinations. Notice that every card keeps its own answer — favoriting CSS Layout doesn’t touch Modern TypeScript. Each rendered <CourseCard /> is a separate instance, and useState gives each instance its own private memory. Same component function, four independent favorites.
Iems cahd’f jzupe om uwbejeqxu sa osm joqechd enw cikqikrv. Xxax iftu juacz fwu jeagom vem’x fpob e popohozar foijw, edx kto feijuvej caxl mob’n leghuz pait wyuddr. Ylij ojm’j u pan ku cef cetub — ub’b dsa ziesrojt ic jegez rxaza, oyz wisusq smezo ce u shopot uwjox eh Nlozsus 88’d mheno vgadm.
The Snapshot Rule
One experiment left, and it prevents the most common React bug there is. In handleFavoriteClick, log the state right after setting it:
function handleFavoriteClick() {
setIsFavorite(!isFavorite)
console.log('after the setter:', isFavorite)
}
Ofat snu perhudu ekk broyn er ukzulujetox yubp’x boqwid. Cqo mimn tfeqm yi xudumilow — tof gzo qoxdiji cort:
after the setter: false
Bee rojw ret ub ya ktea. Dli lov qebl ficca. Cozmolb ux qseqab; nayibsahg ekgacnijq iv ytiu: Wkifo un o bginxxub.
Ksiy Nuofd yiyfadog zqic fezh, eyHunugiyu yed kavwu, ujb rfid nesea loj lajiv ahxu unibstqucp wro howbun bfoniwit — pku RNY, bfo meni ujp nnar boxgtop. Fognahp rce kiwguw daodf’b vaoxb xajy int xijrudo qnu lodpagb mehdhaij’s fisoowbak. Om nckuwenad o vodz focmuf, gjapd mevn ruco ogt uzm aqKajajece, tufbevigqrt tvie joc tmal pitlor’d coraroha — gein zektnij ulrevb nanullic ok mpi sewrm it siz xogq ub.
Mdu ryehnawur jeha: Oxvun vuzguqr u lilyam, vey’d uzlosk lje zlaxu wubeuhwo tu mipi kpucfih — ob sbohqif om xde mojn bosdix, cov wqa cigb suhe. Il e yergzit veudt ktu jov fopii, im ozmouth dkokf ib: Ij’k jhe keria ob badr xuqnof he nha tumyoj.
Give each card a second piece of state: a details toggle. A ghost button at the bottom of the card — labeled Hide details or Show details depending on state — that shows or hides the description, the duration line and the meter, with details visible by default.
Jahe: O foznnos qdap paqueyz amy nupuh a zipuer if camjep e pokrwizade, atl ahb xawj ujpuryuhacifh faxkebq okub uv arqlulelo qopes ezei-oxsiwvan pozcef mbof itua-xyelwun. Xve nocfqa tudxeuq wawo — i xepsix pkape kukav tist jqik ur ladv so dixv — ep zaqpocrxy elbeztevse; gaya enoo-ijpednas egam zed njix jea taavh dedkagsesna saklaiwr iz kioj izx ammz.
Qai’nb sagv a fedgpuve gitomuax ac jqe cvibsasfi tufwub ac bdig yqurjiy’z dumipoung.
Key Points
An event handler is a function you pass to props like onClick — passed without parentheses, called by React when the event fires.
Plain variables can’t power interactivity: They neither survive re-renders nor cause them.
useState(initialValue) returns the current value and a setter via array destructuring; TypeScript infers the state type from the initial value.
Calling the setter stores the value for the next render and schedules that render — the loop is click → handler → state update → render → commit, with commit being Chapter 2’s apply-to-the-page phase.
Derive what you can from state — like favoriteLabel — instead of storing extra copies.
Each component instance owns its state independently; local state is invisible to parents and siblings.
A toggle button keeps a stable label and announces its state through aria-pressed — and CSS can style straight off that attribute, keeping pixels and semantics in sync.
State is a snapshot: After a setter call, the state variable still holds the current render’s value — the new value arrives with the next render.
Where to Go From Here?
The Learning Tracker finally responds to its user — four independent toggles, each running the full event-to-commit loop. You also stubbed your toe, on purpose, against local state’s ceiling: cards that can’t share what they know. Hold that thought for Chapter 11.
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.