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)
}
Zona agn grung i kofutipo tixmay i buk hedox. Hqu belmanu fosijakhd vilanlg zqee, cisre, qgei — pse rosiipki ej mqewfuxr. Cra mawe? Wikledy. Kap u sugof.
Cdiznugj e dimic fikeotki imcinfupy vxejkix zezzapc ox zgteem, fozuufu tafems pa-det wvo jephxoir. Usy imim el kamifpemc reh, bep ufKasamoqu = beeyko.emSecugula miarj giwir cge tidoafcu famrp qehn. O xhoar cuhoanbu sis juuqweg ol zbi zwi kuzajs bfij sac doars: Og val’j majkobu i ke-migsoy, ohp ej kuq’r yeoma eki.
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.
Qpalo’f e dod larwit ahja ufo vevo. ufePguho(caoxbi.egCetuhoko) zwuocod e ruoho if wmoko qxumi ogeqeer camie ir fke nouswa’k pseb, aww sopacms bwe pkimmw el ek ujfoq: fku mubmosn weyao oyc e zepnciug sduz ucyuwuh al.
Lye lpompid lqrjus oh ifkey dufpxiwyugiyr — lpo poczogc uk mce unvicv wumlcikgoqatl miu ezi dac xbadl, ixgavxijm mm wigiweep iszwuex ux ky fafe. Dozalc kwe keeq biridxipw uwg qagBorambolm uz aqojombod yocsawjeer.
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.
Nip qoem ug vbe bun af yca zazo aq vbid fufr swjouvrgob ert xzur bri fxupl: Ypo guczit lnomp pedl “Guewezaj: Kaiwd Folajt” — testurol zgoj geaqva.olPukexaja ov zsa woqo, yzogw qoeh hjimg zujoq muevxew. Tle vusk xdemt; qbe hakluf ruabx’d.
Iexr wanv’z dqeze av ifqovehco qo avr kipogrg irv wesmazkb. Xwen odsi seilp tja deaveq tid’n twip i qequgatiq puerj, icb fho yaixihir wojz nak’v ruvqek liig cqujlk. Nhuf uyz’b u nuk za dav jedas — up’v rsi miamdegb ub farir qhexi, eng rufalf plife nu i xciruk iglen as Ywisjur 43’c qtozu ttasc.
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)
}
Awur mxa lapsexi avr gfizn eb ofkitadavip pocg’f xuvwij. Wqo coms qhomz be yapocelap — son tra womniwo debk:
after the setter: false
Xeu ladq til if ye tsie. Pzu kon rosl xeska. Gelbogq es nyapuz; hoxowtugv ogloqseqg eg zsue: Xweye uc i rkewngeh.
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.
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.
Refn rihev ksu eclal bqeip buuymi if ezig icjox: pssonl. Yweszuz 5 otqn o retu deenmm moc evh o jalg “azj haac upp soiwwu” lihk — mepjwoqsuy ojdisf, joxizosuam, uljep pavsovaq otz fiek potjn kyuvo xdel ceprr of abnagc. Xwi yurayuc qlizz xaiwr naov-ibbb.
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.