Lekce 0.10 · JavaScript pro React
JS vzory, které v Reactu potkáš všude
Seznamy přes map, podmínky v JSX, funkce v onClick, aktualizace stavu a closures v komponentách.
Načítám lekci…
Lekce 0.10 · JavaScript pro React
Seznamy přes map, podmínky v JSX, funkce v onClick, aktualizace stavu a closures v komponentách.
Načítám lekci…
Řádek tasks.map((t) => (t.id === id ? { ...t, done: !t.done } : t)) na první pohled vypadá jako zaklínadlo z Reactu. Přitom v něm React vůbec není: šipková funkce, ternár, spread objektu a metoda pole. Všechno z předchozích lekcí.
Tahle lekce je most mezi JavaScriptem a Reactem. Projdeme si, kde přesně se jednotlivé kousky JS v komponentách objevují – a ty pak v dalších lekcích poznáš na první pohled. Nemusíš ještě rozumět Reactu do hloubky; useState a JSX podrobně vysvětlí lekce 1 a 2. Tady jde o JavaScript kolem nich.
// Komponenta = obyčejná funkce (s velkým písmenem), která vrací JSXfunction Greeting(props) {return <h1>Ahoj, {props.name}!</h1>}// Destrukturace parametru (lekce 0.3) + výchozí hodnota (lekce 0.2)function Greeting({ name, greeting = 'Ahoj' }) {return <h1>{greeting}, {name}!</h1>}// Použití: atributy se posbírají do JEDNOHO objektu props<Greeting name="Jana" /> // props = { name: 'Jana' }<Greeting name="Petr" greeting="Čau" /> // props = { name: 'Petr', greeting: 'Čau' }
// Vytáhni, co potřebuješ, a zbytek pošli dálfunction Button({ variant = 'primary', ...rest }) {return <button className={`btn btn-${variant}`} {...rest} />}<Button variant="ghost" onClick={save} disabled>Uložit</Button>// rest = { onClick: save, disabled: true, children: 'Uložit' } → všechno dostane <button>
Složené závorky v JSX jsou okno, kterým vložíš výraz – cokoli, co má hodnotu. Příkazy (if, for, const) dovnitř nepatří, ty se píšou nad return.
function Price({ amount, discount }) {// ✅ Příkazy a mezivýpočty nad returnconst final = amount - discountconst isFree = final <= 0return (<p>{final} Kč {/* proměnná */}{amount * 1.21} Kč s DPH {/* výpočet */}{formatPrice(final)} {/* volání funkce */}{isFree ? 'Zdarma!' : `Cena: ${final} Kč`} {/* ternár, šablona */}{/* {if (isFree) …} ❌ if není výraz */}</p>)}
// Šablonový řetězec pro složené CSS třídy<li className={`task ${done ? 'task-done' : ''} ${urgent ? 'task-urgent' : ''}`}>// Objekt se styly – vnější { } je okno do JS, vnitřní { } je objekt<div style={{ color: 'red', fontSize: 14 }}>
V JSX není for. Seznam se vykreslí tak, že pole dat přes map převedeš na pole prvků – React umí pole prvků vykreslit:
const fruits = ['jablko', 'hruška', 'švestka']<ul>{fruits.map((fruit) => <li key={fruit}>{fruit}</li>)}</ul>// map vrátí [<li>jablko</li>, <li>hruška</li>, <li>švestka</li>]
function TodoList({ todos, query }) {// filter nad return – odvozená dataconst visible = todos.filter((t) => t.title.toLowerCase().includes(query.toLowerCase()))if (visible.length === 0) return <p>Nic nenalezeno.</p> // early return pro prázdný stavreturn (<ul>{visible.map((todo) => (<li key={todo.id}> {/* key: stabilní id z dat */}{todo.title}</li>))}</ul>)}
| Chci… | Zápis |
|---|---|
| dvě varianty | {isLoggedIn ? <Profile /> : <Login />} |
| něco, nebo nic | {hasError && <ErrorBox />} |
| nic nevykreslit vůbec | if (!user) return null |
| loading / chyba / data | early return pro každý stav |
| variantu podle hodnoty | {ICONS[status]} |
function UserPanel({ user, isLoading, error }) {// early return – každý stav vyřídit a skončit (lekce 0.6)if (isLoading) return <p>Načítám…</p>if (error) return <p className="error">{error}</p>if (!user) return null // null = nic nevykreslitreturn (<div><h2>{user.name}</h2>{user.isAdmin && <span>Admin</span>} {/* && s booleanem */}{user.messages.length > 0 && ( {/* ✅ porovnání, ne holé číslo */}<span>{user.messages.length} zpráv</span>)}<p>{user.bio ?? 'Bez popisu'}</p> {/* ?? pro výchozí text */}<p>{user.address?.city}</p> {/* ?. pro volitelné údaje */}</div>)}
Co vidíš: Malý katalog. V kódu (tlačítko Kód) najdeš filter, map s key, ternár, &&, šablonový řetězec pro CSS třídu, slovník ikon a reduce na součet – všechno okomentované.
Vyzkoušej:
Co z toho plyne: Komponenta je z velké části obyčejný JavaScript: data se připraví nad return (filter, reduce) a v JSX se jen poskládají (map, ternár, &&).
Načítám interaktivní část…
Do onClick se předává funkce, kterou React zavolá při kliknutí (lekce 0.2) – ne výsledek jejího zavolání:
function handleClick() {alert('Kliknuto!')}<button onClick={handleClick}>OK</button> // ✅ předáš funkci<button onClick={handleClick()}>OK</button> // ❌ zavoláš ji HNED při vykreslení<button onClick={() => remove(todo.id)}>×</button> // ✅ potřebuješ argument → obal do šipky<button onClick={remove(todo.id)}>×</button> // ❌ smaže se při vykreslení!
// React předá obsluze objekt události<input onChange={(e) => setName(e.target.value)} /> // text z inputu (vždy string!)<input type="checkbox" onChange={(e) => setOn(e.target.checked)} /><input type="number" onChange={(e) => setAge(Number(e.target.value))} /> // převod (lekce 0.1)function handleSubmit(e) {e.preventDefault() // formulář by jinak znovu načetl stránku// …}<form onSubmit={handleSubmit}>
// Rodič předá funkci jako prop (konvence: on…), dítě ji zavolá (konvence: handle…)function TodoItem({ todo, onToggle, onDelete }) {return (<li><input type="checkbox" checked={todo.done} onChange={() => onToggle(todo.id)} />{todo.title}<button onClick={() => onDelete(todo.id)}>×</button></li>)}<TodoItem todo={todo} onToggle={handleToggle} onDelete={handleDelete} />
const [count, setCount] = useState(0)// useState vrací pole [hodnota, funkce na změnu] – destrukturace pole (lekce 0.3)// jména si volíš sám; konvence je x / setX
Stav se nikdy nemění přímo. Vždy předáš novou hodnotu – u objektů a polí nový objekt nebo nové pole (celá lekce 0.9):
setCount(count + 1) // číslosetName(e.target.value) // textsetUser({ ...user, name: 'Eva' }) // objekt – spreadsetTodos([...todos, newTodo]) // přidatsetTodos(todos.filter((t) => t.id !== id)) // odebratsetTodos(todos.map((t) => (t.id === id ? { ...t, done: !t.done } : t))) // změnit jednutodos.push(newTodo) // ❌ mutace – React změnu nepoznásetTodos(todos) // ❌ pořád totéž pole
setCount(count + 1) // nová hodnota spočítaná z count v TOMHLE renderusetCount((c) => c + 1) // funkce: „vezmi aktuální hodnotu a přičti 1“setTodos((prev) => [...prev, newTodo]) // totéž pro pole
Proč existují dvě podoby? Kvůli closures – viz další kapitola.
Komponenta je funkce a React ji volá při každém vykreslení znovu. Každé zavolání má vlastní proměnné – vlastní count, vlastní handleClick. Funkce vytvořené v jednom renderu si v batůžku (closure, lekce 0.2) nesou hodnoty z toho renderu. Je to jako fotka: zachycuje stav v okamžiku pořízení.
function Counter() {const [count, setCount] = useState(0)function addLater() {setTimeout(() => {setCount(count + 1) // count je hodnota z renderu, ve kterém se kliklo}, 2000)}// Klikneš 3× rychle za sebou: všechny tři timeouty vidí count = 0 → výsledek 1, ne 3function addLaterFixed() {setTimeout(() => {setCount((c) => c + 1) // React předá aktuální hodnotu → výsledek 3}, 2000)}}
Co vidíš: Počítadlo a dvě tlačítka, která přičtou jedničku až za 2 sekundy – každé jiným způsobem.
Vyzkoušej:
count.Co z toho plyne: Když nová hodnota závisí na předchozí a změna se může nakupit (časovače, víc změn za sebou), použij updater setX((prev) => …).
Načítám interaktivní část…
Obsluha události může být async funkce – typicky odeslání formuláře (lekce 0.7):
function SignupForm() {const [isSending, setIsSending] = useState(false)const [error, setError] = useState(null)async function handleSubmit(e) {e.preventDefault()setIsSending(true)setError(null)try {await registerUser({ name, email }) // počkej na serveralert('Hotovo!')} catch (err) {setError(err.message) // ukaž chybu} finally {setIsSending(false) // v každém případě konec načítání}}return (<form onSubmit={handleSubmit}>…<button disabled={isSending}>{isSending ? 'Odesílám…' : 'Registrovat'}</button>{error && <p className="error">{error}</p>}</form>)}
Vykresli pole jmen jako seznam a ukaž, kolik jich je.
VÝZVA: Seznam jmen 1. Místo jediného natvrdo napsaného <li> vykresli všechna jména z pole `names` přes names.map(…). Každé <li> potřebuje key – tady stačí samotné jméno. 2. Do nadpisu doplň počet jmen: names.length
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…
Komponenta s hotovým stavem a vzhledem, ve které chybí JavaScript: filtrování, počítání, přidání, odškrtnutí a smazání položky. Všechno jsou vzory z této části kurzu.
Střední a těžké výzvy jsou bonus pro přihlášené. Registrace je zdarma – stačí e-mail, žádné heslo.
key. Filtruj nad return, prázdný stav ošetři ternárem nebo early returnem.onClick={fn} předává funkci, onClick={fn()} ji hned volá. S argumentem () => fn(x).setX((prev) => …).