Přeskočit na obsah
Kurz Reactu1.0.0 beta
CSEN
Kurz Reactu1.0.0 beta
CSEN
🏠 Úvod🧭 Kde začít?🔁 Opakování📄 Taháky📰 Co je novéhoℹ️ O kurzu
JavaScript0/22
TypeScript0/6

Základy

  • 01 Rychlé opakování základů0/3
  • 02 Stav a události (useState)0/3

Hooky do hloubky

  • 03 Efekty (useEffect)0/3
  • 04 Refy (useRef)0/3
  • 05 Složitější stav (useReducer)0/3
  • 06 Context0/3
  • 07 Vlastní hooky0/3

Formuláře

  • 08 Formuláře a React 19 Actions0/3

Výkon

  • 09 Výkon a renderování0/3

Vzory

  • 10 Návrhové vzory komponent0/3

Ekosystém

  • 11 TypeScript s Reactem0/3
  • 12 Routování (React Router)0/3
  • 13 Načítání dat0/3
  • 14 Správa stavu aplikace0/3

Projekt

  • 15 Závěrečný projekt: Kanban0/2

Shrnutí

  • 16 Shrnutí: principy Reactu

Tvůj účet

Ochrana osobních údajů

React 01 · Základy

Rychlé opakování základů

JSX, komponenty, props, children, podmínky, seznamy a proč na key záleží.

Načítám lekci…

💬 Našel jsi chybu, něco ti není jasné, nebo máš nápad? Napiš mi.
← Typy, které se mění (union a zúžení)Stav a události (useState) →

Než začneme: React je kuchyně s recepty

Tahle lekce je rychlé opakování základů. I když je trochu znáš, projdi si ji – zavádí představy, na kterých staví celý kurz. A ukázka s key na konci chytí i zkušené vývojáře.

Recept místo postupu

Představ si restauraci. Host si neobjednává postupem („rozpal pánev, dej na ni máslo, rozklepni dvě vejce…“). Řekne co chce: „omeletu se šunkou“. Jak se to udělá, je starost kuchyně.

Přesně tak funguje React. Ty mu neříkáš, jak má měnit stránku („najdi tenhle odstavec, přepiš mu text, přidej třídu“). Popíšeš, jak má stránka vypadat, a React zařídí, aby tak vypadala. Tomuhle přístupu se říká deklarativní: popisuješ výsledek, ne postup.

Komponenta je recept, props jsou objednávka

Recept na omeletu je jeden, ale omelet se uvaří stovky – pokaždé trochu jiná: se sýrem, bez cibule, velká porce. Recept tedy počítá s přáním hosta.

  • Komponenta je recept: funkce, která popisuje, jak se má kus obrazovky „uvařit“.
  • Props jsou objednávka: údaje, které recept dostane zvenku („jméno: Jana, role: Frontend“).
  • JSX je hotové jídlo: popis toho, co se má zobrazit.

Ze stejné objednávky má vždy vzniknout stejné jídlo. A kuchař objednávku nepřepisuje – nemůže hostovi změnit, co si objednal. Tohle jsou dvě nejdůležitější pravidla komponent a za chvíli je uvidíš v kódu.

Celá aplikace je skládačka

Velké menu se skládá z menších receptů: „polední menu“ = polévka + hlavní jídlo + dezert. Stejně se aplikace skládá z komponent: stránka obsahuje hlavičku, seznam a patičku, seznam obsahuje položky, položka obsahuje tlačítko. Každá komponenta řeší jen svůj kousek a skládají se do sebe jako kostky.

ℹ️ Co tě v lekci čeká
  1. Od kuchyně ke kódu: první komponenta krok za krokem.
  2. JSX a co se z něj stane.
  3. Props: jak komponentě předat data.
  4. Children: jak komponentě předat celý kus obsahu.
  5. Podmínky: jak něco ukázat jen někdy.
  6. Seznamy a key: jak vykreslit pole dat a proč na klíči tolik záleží.
Každá kapitola začne krátkým povídáním, pak přijde typický příklad rozebraný krok za krokem a nakonec živá ukázka.

Od kuchyně ke kódu

Přirovnání je hezké, ale jak to vypadá v kódu? Projdeme to po malých krocích – od obyčejného HTML až k první komponentě. Žádný krok nepřeskočíme.

Krok 1: hotové jídlo (obyčejné HTML)

Takhle vypadá pozdrav na stránce v čistém HTML:

<section class="welcome">
<h1>Ahoj, Jana!</h1>
<p>Dobré ráno</p>
</section>

Je to hotové jídlo napsané natvrdo. Pořád stejné – pro Janu, ráno. Kdybychom chtěli pozdravit Petra odpoledne, musíme napsat celé HTML znovu.

Krok 2: postup (tak to dělat nechceme)

Bez Reactu bys stránku měnil JavaScriptem krok po kroku – přesně jako host, který kuchaři diktuje postup:

const section = document.createElement('section') // vezmi pánev
const h1 = document.createElement('h1') // rozklepni vejce
h1.textContent = 'Ahoj, Jana!'
section.append(h1)
document.body.append(section)
// …a když se změní jméno, musíš si sám pamatovat, kde je h1, a přepsat ho:
h1.textContent = 'Ahoj, Petře!'

U jednoho nadpisu to jde. U celé aplikace se v tom rychle ztratíš: na co všechno se musí sáhnout, když se něco změní?

Krok 3: recept (první komponenta)

React to otočí. Místo postupu napíšeš funkci, která vrátí, jak má kus stránky vypadat. HTML se přesune dovnitř JavaScriptu – tomuhle zápisu se říká JSX:

function Welcome() {
return (
<section className="welcome">
<h1>Ahoj, Jana!</h1>
<p>Dobré ráno</p>
</section>
)
}
  • function Welcome() je recept – obyčejná funkce. Jméno začíná velkým písmenem.
  • return ( … ) vrací popis jídla: jak má výsledek vypadat. Nic se tu ještě nevykresluje.
  • Uvnitř je skoro stejné HTML jako v kroku 1. Jediný rozdíl hned na první pohled: className místo class (slovo class má JavaScript zabrané).

Krok 4: recept s objednávkou

Zatím umí recept jen Janu. Přidáme mu objednávku – parametr se jménem. Typ za dvojtečkou ({ name: string }) znáš z lekce TypeScript 01: objednávka musí obsahovat jméno jako text. Ve složených závorkách { } pak můžeš do JSX vložit hodnotu z JavaScriptu:

function Welcome({ name }: { name: string }) {
return (
<section className="welcome">
<h1>Ahoj, {name}!</h1>
</section>
)
}

Recept je hotový. Objednávka se píše jinam – do JSX jiné komponenty, která pozdrav chce ukázat. Tady je to hlavní komponenta App, která recept použije dvakrát:

function App() {
return (
<div>
{/* Jeden recept, dvě objednávky → dvě jídla */}
<Welcome name="Jana" />
<Welcome name="Petr" />
</div>
)
}

<Welcome name="Jana" /> vypadá jako HTML tag a tak se komponenta i používá. Atribut name se jí předá jako objednávka – v Reactu se jí říká props (podrobně v kapitole 2).

Krok 5: kdo vaří

Recept sám nic neuvaří. Někde na začátku aplikace (v souboru main.tsx) je jediný řádek, který Reactu řekne: „tady je hlavní recept, uvař ho do tohohle místa na stránce“:

createRoot(document.getElementById('root')).render(<App />)

Od té chvíle vaří React. Zavolá App, ta obsahuje další komponenty (třeba Welcome), React zavolá i ty – a z jejich popisů postaví stránku. Když se později něco změní, zavolá recepty znovu a na stránce upraví jen to, co se opravdu liší. Postup z kroku 2 za tebe napíše on.

Převodní tabulka

V kuchyniV ReactuV kódu
receptkomponentafunction Welcome() { … }
objednávkaprops<Welcome name="Jana" />
hotové jídloJSX – popis výsledkureturn <h1>Ahoj, {name}!</h1>
kuchařReactrender(<App />)
menu složené z jídelstrom komponent<App> → <Welcome> → …
Ukázka

Jeden recept, různé objednávky

Co vidíš: Dvě karty vykreslené stejnou komponentou Welcome. Levá dostává objednávku z políček, pravá má objednávku napevno (Petr, odpoledne).

Vyzkoušej:

  1. Přepiš jméno. Levá karta se změní hned, jak píšeš – recept se „uvaří“ znovu s novou objednávkou.
  2. Odškrtni „ráno“. Změní se jen text pozdravu, zbytek karty zůstává.
  3. Podívej se na pravou kartu: stejný recept, jiná objednávka, jiné jídlo.
  4. Otevři kód tlačítkem Kód. Welcome je obyčejná funkce, která vrací JSX.

Co z toho plyne: Komponenta je recept: popisuje, jak má výsledek vypadat pro danou objednávku. Vaření – i opakované vaření po každé změně – obstará React.

Načítám interaktivní část…

Teď už víš, co komponenta je a k čemu slouží. V kapitole 1 se podíváme, co JSX vlastně je a jaká má pravidla.

1. JSX a první komponenta

JSX z kroku 3 vypadá jako HTML zapsané uprostřed JavaScriptu. Ve skutečnosti je to jen pohodlnější zápis volání funkce. Před spuštěním se přeloží na obyčejný JavaScript, který vytvoří objekt s popisem prvku:

// Tohle napíšeš:
<Button color="blue">Uložit</Button>
// A tohle z toho vznikne:
jsx(Button, { color: 'blue', children: 'Uložit' })
// → { type: Button, props: { color: 'blue', children: 'Uložit' } }

Komponenta vrací právě takový popis. Nic sama nekreslí – vrátí „objednávku na obrazovku“ a vykreslení provede React. Typická první komponenta – definice, kterou napíšeš jednou (třeba do souboru Welcome.tsx):

// ① DEFINICE komponenty (recept)
function Welcome() {
const name = 'Jana'
const hour = new Date().getHours()
return (
<section className="welcome">
<h1>Ahoj, {name}!</h1>
<p>{hour < 12 ? 'Dobré ráno' : 'Dobrý den'}</p>
</section>
)
}

Definice sama nic neukáže. Komponentu je potřeba použít – v JSX jiné komponenty se zapíše jako tag. Tady ji použije hlavní komponenta App:

// ② POUŽITÍ komponenty – v JSX jiné komponenty se píše jako tag
function App() {
return (
<main>
<Welcome />
</main>
)
}
❓ Komponenta je funkce – proč ji tedy nevolám jako Welcome()?

Protože ji nevoláš ty, ale React. Zápis <Welcome /> funkci nespustí. Jen vytvoří popis „tady má být Welcome“ (objekt { type: Welcome, props: {} } z kódu výše). React pak sám rozhodne, kdy a kolikrát funkci zavolá.

Na tom hodně záleží: jen komponenta, kterou volá React, může mít vlastní stav a hooky (příští lekce). Kdybys napsal {Welcome()}, vypadalo by to stejně, ale React by o žádné komponentě nevěděl. Stav uvnitř by se rozbil a fungovaly by špatně i klíče.

Projdeme si, co je na ní důležité:

  • Jméno začíná velkým písmenem. Podle toho React pozná komponentu od HTML tagu. <welcome /> by považoval za neznámý HTML prvek.
  • Složené závorky jsou okno do JavaScriptu. Uvnitř {…} může být libovolný výraz – proměnná, výpočet, volání funkce, ternár. Příkazy jako if nebo for tam ale nejdou; ty patří nad return.
  • Vrací se jeden kořenový prvek. Když potřebuješ vrátit dva sourozence bez obalu, použij fragment <>…</>.
  • Atributy jsou JavaScriptové: className místo class, htmlFor místo for, události v camelCase (onClick) a styly jako objekt (style={{ color: 'red' }}).
💡 Proč je deklarativní přístup lepší
Odstraňuje celou třídu chyb typu „zapomněl jsem aktualizovat tenhle kus stránky“. Stačí správně popsat výsledek z dat – jak stránku upravit, vyřeší React.

2. Props: objednávka pro komponentu

Komponenta Welcome výše umí pozdravit jen Janu. To je jako recept „omeleta pro Janu“. Chceme recept, který pozdraví kohokoli – jméno mu musí přijít zvenku. K tomu slouží props.

Props jsou prostě argumenty funkce. Všechny atributy, které napíšeš na tag komponenty, React posbírá do jednoho objektu a předá ho jako první parametr.

Kód s props má vždycky dvě části na dvou různých místech: ① recept – definici komponenty, kterou napíšeš jednou (většinou do vlastního souboru), a ② objednávku – místo, kde komponentu použiješ v JSX jiné komponenty. Nejdřív recept:

// ① RECEPT – definice komponenty, třeba v souboru ProfileCard.tsx
// Jaké props komponenta přijímá (tvar objednávky):
interface ProfileCardProps {
name: string
role?: string // otazník = nepovinný
avatar?: string
}
// ↓ sem React dosadí atributy, které napíšeš na tag v ②
function ProfileCard({ name, role = 'Vývojář', avatar = '🙂' }: ProfileCardProps) {
return (
<div className="card">
<span>{avatar}</span>
<strong>{name}</strong>
<div>{role}</div>
</div>
)
}

A tady je objednávka. Komponenta App chce ukázat dvě karty, tak do svého JSX napíše dvakrát tag <ProfileCard … /> – pokaždé s jinými atributy:

// ② OBJEDNÁVKA – použití komponenty v JSX jiné komponenty
function App() {
return (
<div className="row">
{/* Dvě různé objednávky, jeden recept: */}
<ProfileCard name="Jana Nováková" role="Frontend" avatar="👩‍💻" />
<ProfileCard name="Petr Svoboda" />
</div>
)
}

Co z objednávky ② doputuje do receptu ①:

Atribut na tagu ②Proměnná v ProfileCard ①U Petra (bez role a avatar)
name="Jana Nováková"name je 'Jana Nováková'name je 'Petr Svoboda'
role="Frontend"role je 'Frontend'role je 'Vývojář' (výchozí hodnota)
avatar="👩‍💻"avatar je '👩‍💻'avatar je '🙂' (výchozí hodnota)

Co se tu děje:

  1. React u prvního tagu v App posbírá atributy do objektu { name: 'Jana Nováková', role: 'Frontend', avatar: '👩‍💻' } a zavolá s ním funkci ProfileCard.
  2. V parametru funkce objekt rovnou rozbalíme (destrukturujeme) na proměnné name, role a avatar.
  3. U druhého tagu chybí role i avatar. Použijí se výchozí hodnoty zapsané za rovnítkem – stejně jako u obyčejné JS funkce.
  4. Výsledkem jsou dvě různé karty ze stejného kódu.
❓ Co znamenají ty složené závorky v parametru funkce?

To je destrukturace, obyčejný JavaScript, ne nic z Reactu (víc v lekci JavaScript 03). Funkce dostane jeden objekt (props) a závorky z něj rovnou vytáhnou jednotlivé vlastnosti do proměnných. Tyhle dva zápisy dělají totéž:

// s destrukturací
function ProfileCard({ name, role = 'Vývojář' }: ProfileCardProps) {
return <strong>{name} – {role}</strong>
}
// bez ní
function ProfileCard(props: ProfileCardProps) {
const name = props.name
const role = props.role ?? 'Vývojář'
return <strong>{name} – {role}</strong>
}

Typ : ProfileCardProps za závorkou popisuje celý objekt props, ne jednotlivé proměnné. Je to přesně pravidlo z lekce TypeScript 01 (kapitola „2. Kde se dvojtečka píše a kde ne“): typ patří za zavírací }, ne dovnitř.

⚠️ Props jsou jen pro čtení
Kuchař hostovi objednávku nepřepisuje. Komponenta props nikdy nemění:
function ProfileCard(props: ProfileCardProps) {
props.name = props.name.toUpperCase() // ❌ mění data, která jí nepatří
const shownName = props.name.toUpperCase() // ✅ spočítej si novou hodnotu
}
Když se má něco na obrazovce měnit, potřebuješ stav – to je téma další lekce.

3. Children: obsah, který dodá někdo jiný

Některé recepty připraví jen obal a náplň nechají na hostovi. Pekař upeče tortillu a ty si řekneš, co do ní. Pekař nemusí znát všechny možné náplně – jen ví, jak tortillu zabalit.

V Reactu je tou náplní speciální prop children. Obsahuje všechno, co napíšeš mezi otevírací a zavírací tag komponenty. Komponenta pak jen rozhodne, kam ten obsah vloží.

// ① RECEPT – karta s nadpisem; náplň dostane v children
function Card({ title, children }: { title: string; children: React.ReactNode }) {
return (
<div className="card">
<h3>{title}</h3>
<div className="card-body">{children}</div>
</div>
)
}

A použití v jiné komponentě. Co je mezi <Card …> a </Card>, dostane Card jako children:

// ② OBJEDNÁVKY – stejná karta, pokaždé jiná náplň
function App() {
return (
<div className="row">
<Card title="Novinky">
<p>Vyšel React 19!</p> {/* ← children první karty */}
</Card>
<Card title="Tým">
<ProfileCard name="Jana" /> {/* ← children druhé karty: */}
<ProfileCard name="Petr" /> {/* obě karty z kapitoly 2 */}
</Card>
</div>
)
}

Card neví nic o novinkách ani o týmu. Řeší jen rámeček a nadpis. Díky tomu ji můžeš použít kdekoli – tomuhle skládání se říká kompozice. Typ React.ReactNode znamená „cokoli, co jde vykreslit“: text, číslo, prvek, pole prvků, nebo nic.

Když komponenta potřebuje víc míst pro obsah (hlavička, tělo, patička), dostane další props typu ReactNode. Takovým místům se říká sloty. Třeba panel s tlačítky v hlavičce a patičkou – nejdřív jeho recept:

// ① RECEPT – panel se třemi sloty: actions, footer a children
interface PanelProps {
title: string
actions?: React.ReactNode // slot v hlavičce (nepovinný)
footer?: React.ReactNode // slot v patičce (nepovinný)
children: React.ReactNode // hlavní obsah
}
function Panel({ title, actions, footer, children }: PanelProps) {
return (
<section className="card">
<header>
<strong>{title}</strong>
{actions}
</header>
<div>{children}</div>
{footer !== undefined && <footer>{footer}</footer>}
</section>
)
}

A objednávka – co se kam vloží, ukazují šipky:

// ② OBJEDNÁVKA
function App() {
return (
<Panel
title="Úkoly"
actions={<button>+ Přidat</button>} // → {actions} v hlavičce
footer={<small>3 úkoly</small>} // → {footer} v patičce
>
<ul> {/* → {children} uprostřed */}
<li>Nakoupit</li>
<li>Uklidit</li>
<li>Zaplatit nájem</li>
</ul>
</Panel>
)
}
❓ Chápu správně, že slot je obyčejná prop, do které dám něco vykreslitelného?

Ano. „Slot“ není nic zvláštního v Reactu, je to jen název pro vzor: obyčejná prop typu ReactNode, kterou komponenta vloží na určité místo ve svém JSX, stejně jako {title}. Do ReactNode se vejde JSX, text, číslo, pole těchto věcí, a taky null, undefined a false, ze kterých se nevykreslí nic. Proto může být slot nepovinný.

  • children je taky slot. Jen se zapisuje jinak: obsah píšeš mezi tagy místo children={…}.
  • Slot vs. datová prop: u title: string rozhoduje o vzhledu Panel (<strong>{title}</strong>). Do slotu dostane hotové UI a jen ho umístí. Slot použij, když má vzhled určit ten, kdo komponentu používá.
  • Prázdný slot s obalem: {footer} při chybějící patičce nevykreslí nic. Ale pokud kolem slotu kreslíš rámeček nebo čáru, musíš podmínku napsat sám: {footer !== undefined && <footer>{footer}</footer>}. Podmínka je skutečný boolean („patička byla předána“) – samotné {footer && …} by spadlo do pasti s nulou z kapitoly 4, kdyby někdo do patičky poslal číslo 0.
Ukázka

Props, výchozí hodnoty a children

Co vidíš: Dvě karty vykreslené stejnou komponentou ProfileCard. První dostala všechny props a navíc obsah mezi tagy (children). Druhá dostala jen name.

Vyzkoušej:

  1. Porovnej karty. U druhé se doplnila výchozí role „Vývojář“ a avatar 🙂, protože je objednávka nezmínila.
  2. Všimni si textu „Píše React od roku 2018“ u první karty. To je children – komponenta ho jen vložila na připravené místo.
  3. Klikni na </> Kód a najdi řádek {children !== undefined && …}. Díky němu druhá karta, která žádnou náplň nemá, nevykreslí ani prázdný obal.

Co z toho plyne: Jeden recept, různé objednávky. Props mění údaje, children dodá celý kus obsahu, a komponenta nemusí vědět, co v něm je.

Načítám interaktivní část…

4. Podmínky: něco ukázat jen někdy

Recept často obsahuje podmínky: „když je host vegetarián, vynech šunku“, „když nemáme vejce, nabídni palačinky“. V JSX se podmínky píšou obyčejným JavaScriptem – React žádnou zvláštní syntaxi nemá. Jen si musíš vybrat správný zápis podle situace.

Typický příklad: ikonka schránky s počtem nových zpráv.

function Inbox({ user, count }: { user: User | null; count: number }) {
// 1) Early return: když není přihlášený, zbytek receptu nás nezajímá
if (!user) return <p>Přihlas se, prosím.</p>
return (
<div>
{/* 2) && – buď něco, nebo nic */}
{count > 0 && <span className="badge">{count}</span>}
{/* 3) ternár – jedna ze dvou variant */}
<p>{count > 0 ? 'Máš nové zprávy' : 'Nic nového'}</p>
</div>
)
}
ZápisKdy použít
if (!data) return …Loading, chyby, prázdné stavy – komponenta skončí dřív. Nejčitelnější.
cond && <X/>Buď něco, nebo nic. Podmínka musí být boolean!
cond ? <A/> : <B/>Dvě varianty UI.
const map = { a: <A/>, b: <B/> }Víc variant podle hodnoty (místo řetězu ternárů).

Past s nulou

Proč ta poznámka „musí být boolean“? Zkusme místo count > 0 && … napsat kratší count && …:

{count && <span className="badge">{count}</span>}
  1. Operátor && v JavaScriptu nevrací true/false. Vrací levou stranu, pokud je „nepravdivá“, jinak pravou.
  2. Když je count 3, levá strana je pravdivá → výsledek je <span>. V pořádku.
  3. Když je count 0, levá strana je nepravdivá → výsledek je číslo 0.
  4. React false, null a undefined nevykreslí. Ale čísla vykresluje – takže na stránce se objeví osamělá „0“.

Řešení: dej do podmínky vždy skutečný boolean – count > 0, items.length > 0, !!value.

Ukázka

Past s nulou a early return

Co vidíš: Nahoře tři řádky, které zobrazují počet zpráv třemi způsoby: špatně (count &&), správně (count > 0 &&) a ternárem. Dole odznak uživatele napsaný s early return.

Vyzkoušej:

  1. Hned na začátku je count 0. V řádku „Špatně“ uvidíš osamělou 0, řádek „Správně“ je prázdný.
  2. Klikni na Přidat zprávu. Teď oba řádky ukazují stejný text – chyba je vidět jen při nule, a proto se snadno přehlédne.
  3. Klikni na Vynulovat. Nula je zpátky. Ternár mezitím přepíná mezi 📬 a 📭.
  4. Dole klikni na Přihlásit a Odhlásit. Komponenta UserBadge při chybějícím uživateli skončí hned na prvním řádku a zbytek vůbec neřeší.

Co z toho plyne: Do && patří jen boolean. Pro dvě varianty použij ternár, pro „nic nekresli / načítám / chyba“ early return.

Načítám interaktivní část…

5. Seznamy a proč na key záleží

Představ si šatnu v divadle. Odevzdáš kabát a dostaneš lísteček s číslem. Po představení podle čísla dostaneš zpátky svůj kabát – i kdyby šatnářka mezitím věšáky přeskládala.

Co kdyby šatna místo čísel fungovala podle pozice: „váš kabát je třetí zleva“? Dokud se nic nehne, funguje to. Jakmile ale někdo pověsí kabát na začátek řady, všechno se posune a ty odejdeš v cizím kabátě.

Přesně tohle řeší v Reactu key. Když vykreslíš seznam, React si mezi rendery musí pamatovat, který prvek je který – hlavně proto, že prvky můžou mít vlastní stav (text v inputu, rozbalený detail). key je ten lísteček s číslem.

Příklad: vykreslení seznamu

// Data – pole úkolů
const tasks = [
{ id: 17, label: 'Nakoupit' },
{ id: 42, label: 'Uklidit' },
]
// ① RECEPT – jeden řádek seznamu
function TaskRow({ task }: { task: { id: number; label: string } }) {
return <li>{task.label}</li>
}
// ② POUŽITÍ – seznam: map udělá z každého úkolu jeden <TaskRow>
function TaskList() {
return (
<ul>
{tasks.map((task) => (
<TaskRow key={task.id} task={task} />
))}
</ul>
)
}

Co je v ukázce pevně dané a co sis pojmenoval sám? Pevná jsou jen dvě slova – key a map. Všechno ostatní můžeš pojmenovat po svém:

SlovoKdo ho určujeCo znamená
keyReact – musí se jmenovat přesně takhlelísteček s číslem; React si ho vezme pro sebe a komponentě ho nepředá
mapJavaScript – metoda polez každé položky pole udělá jeden prvek
taskstypole s daty – klidně todos nebo items
tasktyjedna položka, na které je map právě teď (parametr callbacku)
id, labelty (nebo databáze)vlastnosti položky; id se hodí jako klíč, protože je u každé jiné
TaskRowtykomponenta pro jeden řádek – jen musí začínat velkým písmenem
task={task}tyobyčejná prop: vlevo její jméno, vpravo proměnná, kterou do ní posíláš
  • Seznam se v JSX vykresluje obyčejným map: z pole dat uděláš pole prvků. (Jak map, filter a další metody polí fungují, vysvětluje lekce JavaScript 04.)
  • key se píše na prvek, který map vrací. Musí být jedinečný mezi sourozenci a stabilní – stejná položka musí mít při každém renderu stejný klíč.
  • Nejlepší klíč je ID z dat (z databáze, nebo vytvořené ve chvíli, kdy položka vznikla).
❓ Je key obyčejná prop? Můžu si ho v komponentě přečíst?

Nemůžeš. key (stejně jako ref u starších verzí) si bere React pro sebe a komponentě ho nepředá. TaskRow výše v props najde jen task. Když komponenta ID potřebuje, předej ho zvlášť: <TaskRow key={task.id} id={task.id} … />, nebo ho vezmi z dat (task.id).

A ještě jedna věc: key patří na prvek, který vrací map, ne dovnitř komponenty. Psát ho na <li> uvnitř TaskRow nemá smysl, React ho hledá v poli, které vzniklo z map.

Co se stane s klíčem podle pozice

Řekněme, že každý řádek má vlastní input na poznámku. Do řádku „Nakoupit“ napíšeš „mléko“. Pak přidáš nový úkol „Vyvenčit psa“ na začátek. React porovnává staré a nové řádky podle klíče:

KlíčPřed přidánímPo přidáníKde je poznámka „mléko“
key={index}0 = Nakoupit, 1 = Uklidit0 = Vyvenčit psa, 1 = Nakoupit, 2 = Ukliditu „Vyvenčit psa“ – řádek 0 si nechal svůj stav
key={task.id}17 = Nakoupit, 42 = Uklidit99 = Vyvenčit psa, 17 = Nakoupit, 42 = Ukliditu „Nakoupit“ – řádek 17 se jen posunul

S indexem si React myslí, že řádek 0 je pořád tentýž řádek, jen dostal jiný text. Ponechá mu tedy jeho stav – poznámku „mléko“. S ID ví, že řádek 99 je nový, a řádky 17 a 42 jen přesune i s jejich stavem.

Ukázka

Bug s key={index}

Co vidíš: Stejný seznam úkolů vykreslený dvakrát. Vlevo s key={index}, vpravo s key={task.id}. Každý řádek má vlastní input – jeho text je stav řádku. Tlačítko přidá nový úkol na začátek obou seznamů.

Vyzkoušej:

  1. V obou seznamech napiš do inputu u „Úkol 1“ slovo „jedna“ a u „Úkol 2“ slovo „dva“.
  2. Klikni na Přidat nahoru. Vlevo se poznámky „posunou“ ke špatným úkolům: „jedna“ je teď u úkolu 3. Vpravo zůstanou u svých úkolů a nový úkol má prázdný input.
  3. Přidej ještě jeden úkol. Vlevo se chyba prohloubí, vpravo je všechno v pořádku.

Co z toho plyne: Stav patří prvku s daným klíčem. Klíč podle pozice přiřadí stav špatnému prvku, jakmile se pořadí změní.

Načítám interaktivní část…

📍 Kdy smí být klíčem index
Jen u seznamu, který se nikdy nepřeuspořádá, nefiltruje, nepřidává se do něj doprostřed ani na začátek a položky nemají vlastní stav – třeba statický seznam odkazů v patičce.
⚠️ Klíč nikdy negeneruj při renderu
key={Math.random()} nebo key={crypto.randomUUID()} je jako dát šatnářce při každém pohledu na věšák nový lísteček. Každý render by vytvořil úplně nové prvky a zahodil celý jejich stav. ID vytvoř ve chvíli, kdy položka vzniká (při přidání), a ulož ho k datům.
✨ Trik: key jako reset

key nefunguje jen v seznamech. Můžeš ho dát na jakýkoli prvek a React se podle něj rozhoduje stejně: stejný klíč = tentýž prvek (nechá mu jeho stav), jiný klíč = jiný prvek (starý zahodí i se stavem a vytvoří nový). Toho se dá využít, když chceš komponentu „vynulovat“.

Příklad: stránka ukazuje formulář pro psaní zprávy vybranému uživateli. Vybereš Janu, do formuláře napíšeš „Ahoj Jano“ a pak přepneš na Petra.

// ❌ Bez klíče: na tomhle místě je pořád „ten samý“ MessageForm,
// jen dostal jiné props – rozepsané „Ahoj Jano“ v něm zůstane i u Petra
<MessageForm userId={userId} />
// ✅ S klíčem: pro každého uživatele je to jiný formulář
<MessageForm key={userId} userId={userId} />

Co React udělá s klíčem po přepnutí z Jany na Petra:

  1. Dřív byl na tomhle místě MessageForm s klíčem 'jana', teď je tu s klíčem 'petr'. Jiný klíč → pro React to není tentýž prvek.
  2. Formulář s klíčem 'jana' zahodí – i s rozepsaným textem.
  3. Vytvoří nový formulář s klíčem 'petr', prázdný, jako by se stránka právě načetla.

V řeči šatny: nový lísteček = nový kabát. Petr nedostane Janin kabát jen proto, že visí na stejném věšáku. Hodí se to všude, kde má komponenta při změně „o koho nebo o co jde“ začít znovu od nuly – formulář, detail produktu, přehrávač. Tlačítko „↺ Reset“ u ukázek v kurzu funguje přesně takhle: změní ukázce klíč.

Ověř si to

Kvíz Co vykreslí {items.length && <List items={items} />}, když je pole prázdné?
Kvíz Komponenta dostane prop title. Potřebuje ho zobrazit velkými písmeny. Co je správně?
Kvíz Kdy je v pořádku použít index jako key?

Rozcvička

Výzva

Jmenovka

Rozcvička

Komponenta už props dostává – jen je ještě nevypisuje.

Zadání najdeš v komentářích v kódu níže – na začátku souboru a u míst označených TODO.

🧩 Odkud se berou věci, které tu nejsou definované
CSS třídy jako row, stack, card, list, btn, input nebo muted jsou hotové styly kurzu v src/styles.css (row = prvky vedle sebe, stack = pod sebou, card = rámeček, muted = šedý text).

Načítám interaktivní část…

Výzvy

Výzvy jdou v pořadí kapitol. První procvičí kapitolu 3 – napíšeš „obal“, který neví nic o svém obsahu. Druhá kapitoly 4 a 5 (podmínky, seznamy a klíče).

Výzva

Panel se sloty

Lehká

Napiš „tortillu“: panel s nadpisem, který přijme libovolnou náplň přes children a volitelné sloty pro tlačítka a patičku. Tenhle vzor uvidíš v každé UI knihovně. Co se má na konci ukázat, je v komentářích v kódu.

Zadání najdeš v komentářích v kódu níže – na začátku souboru a u míst označených TODO.

🧩 Odkud se berou věci, které tu nejsou definované
CSS třídy jako row, stack, card, list, btn, input nebo muted jsou hotové styly kurzu v src/styles.css (row = prvky vedle sebe, stack = pod sebou, card = rámeček, muted = šedý text).

Načítám interaktivní část…

Výzva

Seznam produktů

Lehká

Vykresli seznam produktů s cenou, vyprodané zvýrazni a přidej filtr „Jen skladem“. Checkbox už je připravený (používá stav, který podrobně probereme v příští lekci). Co se má ukázat, je v komentářích v kódu.

Zadání najdeš v komentářích v kódu níže – na začátku souboru a u míst označených TODO.

Načítám interaktivní část…

Shrnutí

  • React je deklarativní: popisuješ, jak má stránka vypadat, ne jak ji změnit.
  • Komponenta je recept – funkce, která z props vrátí JSX. Jméno s velkým písmenem, jeden kořenový prvek, výrazy v {…}.
  • Props jsou objednávka: přicházejí zvenku a komponenta je jen čte.
  • Children a sloty (props typu ReactNode) dovolí komponentě obalit obsah, o kterém nic neví.
  • Podmínky: early return, ternár, nebo && – ale do && jen boolean, jinak se vykreslí 0.
  • Key je lísteček z šatny: určuje, komu patří stav. Stabilní ID z dat, index jen u statických seznamů, nikdy náhodný klíč.
  • Změna key komponentu resetuje – užitečný trik.
🧩 Odkud se berou věci, které tu nejsou definované (products, Product)
products, Product – ze souboru src/course/fakeApi.ts. Falešné API kurzu: data a funkce, které předstírají server (se zpožděním, občas i s chybou). Ve skutečné aplikaci by tu bylo volání fetch().
export interface Product {
id: number
name: string
category: 'ovoce' | 'zelenina' | 'pečivo' | 'mléčné'
price: number
inStock: boolean
}
export const products: Product[] = [
{ id: 1, name: 'Jablko', category: 'ovoce', price: 12, inStock: true },
{ id: 2, name: 'Banán', category: 'ovoce', price: 8, inStock: true },
{ id: 3, name: 'Hruška', category: 'ovoce', price: 15, inStock: false },
{ id: 4, name: 'Mrkev', category: 'zelenina', price: 6, inStock: true },
{ id: 5, name: 'Rajče', category: 'zelenina', price: 9, inStock: true },
{ id: 6, name: 'Okurka', category: 'zelenina', price: 19, inStock: false },
{ id: 7, name: 'Rohlík', category: 'pečivo', price: 3, inStock: true },
{ id: 8, name: 'Chléb', category: 'pečivo', price: 45, inStock: true },
{ id: 9, name: 'Mléko', category: 'mléčné', price: 22, inStock: true },
{ id: 10, name: 'Sýr eidam', category: 'mléčné', price: 39, inStock: true },
{ id: 11, name: 'Jogurt', category: 'mléčné', price: 14, inStock: false },
{ id: 12, name: 'Pomeranč', category: 'ovoce', price: 11, inStock: true },
]
CSS třídy jako row, stack, card, list, btn, input nebo muted jsou hotové styly kurzu v src/styles.css (row = prvky vedle sebe, stack = pod sebou, card = rámeček, muted = šedý text).