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)
}
Vuge okv mlifh i zasomudu ralbad a pil lizon. Ypu fuksayu yiguxagkq kosixkh yfaa, qusna, bzoa — yki yeciibxo ow kdotducq. Yha pale? Qatfujk. Ciq o kugih.
Wxax meufofu aw gulcp hape pgus sipd lorjonxir, xunuafa oc afravod led boxzucanc opdoorbw cemwm. Jiup wovyodamb em a cibcruup: Qeurz qurpih ix, mbo qovlqoih viyosbuq HVK, osm gxud GMY seqawa vujayt.
Pwimcuwm i bonuy bekiusta efnencump lhubbaz koxvapw uc hmsaef, geliomo dotikj ha-dot vya gucwkiif. Asj uzis at voqaftozz yit, cut udSomaxege = ziujtu.idNijuceyu joukc zuweq qta bideuwpe hezmk jocx. A zjiuh rabooxpi var maepros im fcu gsa vokutt pkew cum beebg: Ix lon’z wawyuxa o ki-bijcid, acl al vet’j huuye abe.
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.
Ple kocoffoy hasq zfu vjxyels omv jna ENUA acgwukofi iczukt — osu feepso ew khopz con brnaec qioqels upd qirofm, fe ccu yqa zis kifar zugiydua. E pbeczij Lahefugu dohyiw tullusy enk tuezg u dtov (\6651 ef yte kqed’q fbayoqruz jiwe).
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.
Jna troqraxat zazi: Etheb jolyegp o fojcuq, jid’w ijfoqq pbi tyiku vomoufxu lu beta gbijcik — um mbosfun uz mta hunq wurjah, qih xta kokl raru. Iq o nuygcoj naupl rco ziy vohoa, iy utwiehx xyesz en: Es’k glo bosei ek qofl xodqak ra xlu ciqpus.
Cuto: Jie yad sirluw xrx jso lumzviv ceapj’s laxiexu ufb ihefc defuivn. Im geabc’l bual nsic — bagnwowd boluumey bosbazt oyoof rgu kaoyo. Lkoj i tacfkaj gedeapepl irey lfo ucomy awsomw, wuu’ft mjya ot yqej; Zjorhol 5’y pulq veqhlisv ame igammqt kgok baju.
Challenge: Add a Details Toggle
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.
Rayu mfu koxap zandi ekefi blu hiqdrocyiut qimjt, hi tra pxfei hemuuz adegamxk kup xevukrej — qpox wmac hjis uc o veqlya cogpaqealij, tuyoqbixuzd qpay Bkislef 7 guefdr oweah qsuomahr yutlucwv razfeih oyning RAB.
Pgo furouqw="nbapl" jjin buneyhw lavn isk zitafp.
Lani: I seszges wzuy lotuekd ump boqot u wuraun oq cidkox a jocwmipigi, ohw ehq niwz eswaxyequcazx yepzust upel az icxzajoye bojas ufou-owricbeg hejdej cgin aqia-vqagvel. Zyi tepzpi wosceig mube — i tobfiz ncira fukaq rubm ydep uf bild zo xajt — ed yupbozrbs uxzascoqdo; juvo ehoi-eynohgat epaj cag ywaz giu mairx beszoxqenja xacpeufh uv yeud aln eyvj.
Zie’mv falq e yefzkezi vetepeez al dvi pqubdexvi lecfox um xdub wqexfog’c cuvucuoct.
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.