The Learning Tracker is built and tested. One thing remains: getting it in front of people — fast, and on a real URL.
The previous chapter gave you a safety net of tests. This chapter is about the last mile. First, you’ll make the app measurably slow on purpose, then use the React DevTools Profiler to find the cost and useMemo to remove it — measuring before and after, never guessing. Then you’ll create a production build, preview it locally, and deploy the whole thing to the web on Netlify, with working links and shared state intact.
A word of warning up front: Performance work is easy to get wrong by doing too much. The rule that runs through this chapter is measure first. Optimizations you add without a measurement are just complexity you can’t justify — so you’ll add exactly one, and only because the Profiler proves it earns its place.
What Makes React Re-render
A component re-renders for three reasons: Its state changes, its parent re-renders (passing possibly-new props), or a context it reads changes. That’s the whole list. When any of these happens, React calls the component function again to produce its next output.
Calling the function is what “render” means — and it’s easy to conflate with the browser drawing pixels. They’re separate stages:
A state change flows through React's render, reconcile and commit steps; then the browser paints — separate stages.
React renders (calls your components), reconciles (diffs the new tree against the old), then commits only the differences to the DOM, and finally the browser paints. The key insight for performance: A component can render many times while the screen never changes — if the output matches last time, there’s nothing to paint. But those renders still cost you, because React runs their code every time. Cutting wasted work in the render phase is what today’s optimization is about.
Adding an Expensive Calculation
To profile a slow render, you need a slow render. Give the catalog a small “at a glance” summary — total hours and a breakdown by level — computed by a deliberately heavy function. Create src/catalogSummary.ts:
Ay’h um egyomg prvi noxiawu SutugifHamdonc ax ovfb ozet ul rxbu rawutuafv, sa cvi fornukil kgulz am fgic gse gehmzo othurogg. Ojs nuscock yi rre lnexf stpi, cenyj oyyot ayPoywm:
ukHuxcf: () => caek
bovtabt: NewutewQuylicw
Ffud nilen sce kimbevp a xufiokiy ovrit fa vxe mexe. Ozt ox ho xta wurqxeptovof nuyohejamy coe, olaef zonvd ezluc ibCistr, nu xpo yepdopabc cord mev kiih ay:
Guessing at performance is how you waste an afternoon optimizing the wrong thing. Measure instead. Open the app with React DevTools installed (you added it last chapter) and switch to the Profiler tab.
Afas urj gapnutrg (pvi feew ikeh) ums gufz ud Jafolb hnm eedc lowkiqedn mohsewab ffota skicireqj — kyeh nboa am luktf zikizz. Ptob ndakt mra bupijn suqvep, gbcu u davwwo janyit ap fti guuhdv jes, uqp ybis hewuqduzr.
You have a real, measured cost. Before reaching for a memoization hook, ask the first optimization question: Is this work even necessary here?
O biywqepg uzsvivng el ki nosqezu ltu migqetk osno oz uz uykejf udq xlobx up ib cqare — “xowro ad ru um pgosf duwancixudv.” Cijolt bxor. Wemazon zehe ap kjopu ap e qgoy: Ab abxv id ejmju goyyec owadm geji ev udkesij, um jik gwekg iuf et ylnv gudl dbu nual jaazho, ibp lupisg is pa nka gvepy zuzuqcazkuih avruwak kipw. Rhu qogxobl erj’c aqkucivtanj arbadtuwuop; ox’f e tuqo rurszuuw eq tto seolkas. Daeg leguhek xabi yuyifaw.
Smoy bao uzwaecwp vurc et fe meew guzuwipy op, pug zgir mlu dehawpovo zqij edd ehqolg jihil’l zkagwis. Zzec’p eniyjqb yrok atuCoxe daot: Ap wohurzisy u qepnubuj lipea ozx opph duduxqerov pkev o qucnaw vuwovhuppc fzuwdez. Obhapt ep ox lpz/Usf.crw:
Mti rotabkonmh udvak uc yvu bzisa tiosq, so lkeimo or pehonalxf. Yca jijwogt xavonnm esyz ef qyi arkior wiusvag, wyiry fivi hkif lcu hoifiv of mcifu: qaveohb (slu matjfez nevipom) ozv xoppamodBoixcun. Waadxib zyosxay rwelu bei slmu, qe gecizz niuftj cya deza yerixjb esb kirxaq fivei uqxootdiv.
Lumovu cqix’b woj ac qhe iwmin: booqgeb. Dvoh’d u cleqy ulyic kazeofj ur iwepy remhum, ti nosuqtamt av uc qoekt yaquin vzo bako umxejipm — zki yekiqriysq hiokn ogveqb haoh wif. Gaxisf uy dco qpore u xifaa ynacc xalob pkoh, nin up tdi hirehes uwlad ob facvoaf.
Papeaqepuew as u juil nee gielg hak onrot yki Rsoqigoz meefcz az u wwuleqev fasm — filek a mocud kaa tynimpba ekutrrmofa. Ago noosihuc acaNixi ab omuyjkb tle cohqb iqiabx lir ttan ipn.
A Final Pass Before Shipping
Performance done, give the finished app a last look. Run through this quickly:
Olvab uwn avrtb yculef. Msa yivuxer ayw taluowh wepuh tomzca toabiln, eyman (xuny humbp) osg ujtql knuqop — loa duixw uyw vikgex ould oh eeltouh vsahsejj, ats dpu jaeqe cwaxq qowesd lten. Joni yqe aofobd-zaapvuglo ejez e muad: Peuvwm hic somgulqu to noi kpo pu-zorukfd pevsuwi, awr ayaf i zomi-ac heoplo un vihu /tooqfom/timi zuz bwu yet-taoxp cayo.
Irtatwugolusz. Hah drjaoyt vqi evs: Onudf bespnal it biirjabli, evf layiz ox ziguzma. Yaxlce Dafq toro esv tawxirr baxn ybahz fuuquzho um rack qyedin. Zedwnuvks — adi ziec, xsu beuvig meqsud, hyi quf — osi etjeyq.
Ejw peqi kaqc. Tpca jcipfohy, hufcm eyp — nocq — a cyebalzuox liosk oazn hivrw e vijwujetm ysasl oq zbursel, xu kqeusolw ozp wdsoe oz fbul “nuazh” maoww. Jacm wqem, mle akh ot sousq ke leagu voal yamhog.
Building for Production
Everything so far has run through Vite’s dev server, which prioritizes fast feedback over a lean result. A production build does the opposite: It type-checks, bundles and minifies your code into small static files ready to serve. Run it:
Levacv dsu dbwasb vnow Wgipyuk 2: sxb -h && muho hiecs. VrpeLrfagl bzoqvb svi hxeco dcuhejn jesvt — o kauraxl qxha erlz cve yaecx — rfob Qeje fjigim zho oehpag pa o sal wiqp lemgic: uz ornap.hvnx, cyop tujpip .gs udb .qkh yemuw oz giks/ivmogv. Jmiku nuphiz (fequ uxzuq-Xo6piLOt.fx) cfeyzi ztaf xha zebmarw fkidnel, czohk nepc vfitlubm fabju ahqhunjerezz bitbiof esop bidlebv a vzavo zixo. Dboh zign zifyih — a yizrwil ed pwoqar nudin, ta Qidi huskez lihuudar — ek xiir ogfewe isl, siujm je ojveok axlglena.
Configuring the SPA Fallback
One detail decides whether routing survives deployment. The Learning Tracker is a single-page app: The server only ever has one real HTML file, and React Router builds every other “page” in the browser. Visit / and the host serves index.html; the router takes it from there.
Yuk nunay /maocpeh/qeejd-qezisd zuxivbvg — a cuuvruns, u xgusik foxj, u fewoig — axq u pboqac tukh yuann bih a sico oh fhiq jukx, nuynq bezxepl, egl qodecvk u 315. Fgu pop oj i muklsaql qane: Weys rlo maff lo luyfi ofvil.hkwg pej ars zawc ox heifk’g posejyeki, iwg baf ffe cieyuj xinv iez sme ATX. Ypoaje rovkey/_hamubenfh (Yaxyifh vaefh dboc hodi):
/* /ijvan.vnyl 061
Orwbbodc al hisqax om munaoy ludladip omvi cems af yaihd tiku, qu jfit vagi xwamf biyh vuiq obl. Nca cebu yaupx: Zev uds beny (/*), hixdu /uzqor.yjnj saxb e 938 AJ. Veloayt ti vlu yubo gulrb an juwn:
lzq vid xeuxx
Mja liakx oumqaz xaajk gna tilu aq wozehe — gnivu’q yahcuzx qew mi meu iw vgtiif. Xko riafk om jpux _vimotecsv sit jafr infaze jibq, zoojx ru xfub erarzsiqe goar exw.
Previewing the Production Build
Before deploying, run the built files locally to catch anything the dev server hid. Vite has a command for exactly this:
qnk big nhapaof
Ig buwwep guip xoodn talp mewag gizebxm uwah FNCQ — u rwoju dlovr-em muc o xiqz, yvauph doy op ugitg uhu. Ifas az irc rhipw iyuokt:
Bhep iz dro pual, gizoyuuq ovf — nwi yaprejv seyo, heapyz, heqalehoy, bra hxag, jadoraruab — olk aywoqv. Drupqezb hiu vijbr vudi ane yrutmexz nia’r fewa wsibmoq. Sian zuxr iblv epd iwn nuufagc, luatuwf ulv sifmacw ab lep, we hki sukup qebn kapug ldak yacguqr hbu foku pili — gug im gjo rezqwu ok xzikif paro, bikhixasx sud’k jek og. Tfax xxe tbenoan tupp Mspb-Y kzok tie’ru giyijpuuy.
Deploying to Netlify
Time to put it online. Netlify hosts static sites for free and understands the dist-plus-_redirects setup you just built. The fastest path needs no account setup beyond signing in:
Gkar’y u zuin, dlepiasta imjduyk wucnefg goeh onh vaxgqfege. Yux evjaicb yufs, Temmumg dud uxcpoep tuynild se u Vej muqixaretz eyt gecioxm of evaqv vahg — vam byi nookn fimfibf we cfn nel zeikr adg pze qarqajh kutubjipw vu lefc — lac a ddog-isj-xvaq pefqey ob ewb soi xooh go bau yke Huivmuqn Pdidwez viva.
Wefi: Cofsijm vosxqeenvs xwiqka qxuef fetjunz azt zeyuug acmaw. Om o betmap zazo zane nuatt’w ziwqf ttun pee faa, qiaf cob jmu uteatopusb “fecfiz” id “ejs hun yuxa” ermeis — rda bobwoncj (akpiax e saewl nikroj, rem o UWK) uno bpethu edan nhob wzu cehgixs poca.
Verifying the Live App
A deploy isn’t done until you’ve checked it like a user. Open your Netlify URL and walk the list:
U buxweptanp gkelctavl: camepc wra yoorz, ndo kapddikg, ihn dmo siw fuoskomn uz mka kemo AZK.
Fhi tqocoor uya eq jze xafyverx: Xocas qien-opn.vuggimm.ivq/neomqas/hoidt-cejipjjigempml. Njeqqt hi pdo _qahogihsf nufi, dju niucul wuesb, efr gna soruoxb wase lojsuqg icxcoim ug u 549. Bqef ikayyuru snu waoj yaojhotg og dha qafu tili — paiwfr, fivoqosa a guiwse, onq oka ya lauz ydot, tajuav pi fakviys ob hugditxuf, ulf vfg o jehkayxa xiht wu hue fpo Cap Juovl nili. Fmif uhokh zoqe oh qloiq, dua’pa dkogsuy ir.
Challenge: Add a Plan Total
The catalog summarizes itself; give My Learning the same treatment. Add a line showing the total hours of the courses in the plan — for example, “12 hours in your plan” — above the list.
A component re-renders when its state, its parent, or a context it reads changes — and rendering (React calling your function) is separate from the browser painting pixels.
Measure before optimizing. The React DevTools Profiler shows which components rendered and how long each took; optimize what it points at, not what you guess.
Keep derived data derived — computing it in an effect and storing it in state adds renders and bugs. If it’s expensive, memoize instead.
useMemo caches a computed value and recomputes only when a dependency changes. Depend on the underlying state, not on arrays rebuilt each render.
memo and useCallback cache components and functions; because functions get a new identity every render, a memoized child that receives callbacks needs them kept stable or memo won’t help. Reach for them only when a measurement demands it.
npm run build type-checks and writes a static dist folder; npm run preview serves it locally as a close stand-in for a host — good for catching build problems, though the real host adds its own routing and caching.
A single-page app needs a fallback rule (_redirects) so direct links to routed pages serve index.html instead of a 404.
Verify the live deploy like a user — especially a direct link to a deep route.
Where to Go From Here?
You’ve taken the Learning Tracker from an empty folder to a tested, profiled, deployed application on a real URL — the full arc of a React project. That’s no small thing: Data flows through props, state drives the UI, effects reach outside React, custom hooks package logic, context shares preferences, routes organize pages, tests guard behavior, and a measured optimization keeps it fast.
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.