Proč React?

React (vytvořen Meta, 2013) je dnes de facto standard pro tvorbu komplexních webových aplikací. Nejedná se o framework — je to knihovna pro UI. Vše ostatní (routing, state management, fetching) si volíte sami.

VlastnostVanilla JSReact
UI aktualizaceManuální DOM manipulaceAutomaticky přes Virtual DOM
StrukturaFunkce + globální stavKomponenty = self-contained jednotky
ŠkálovatelnostSložité u velkých appekKomponentová hierarchie škáluje dobře
KomunitaNejvětší JS ekosystém
Pracovní trhNejžádanější frontend skill

Virtual DOM — jak React funguje

React nemanipuluje DOM přímo. Udržuje virtuální reprezentaci (VDOM) a při každém re-renderu porovná nový VDOM se starým (reconciliation — diffing). Pouze skutečně změněné části DOM pak aktualizuje. Výsledek: minimum DOM operací = rychlý UI.

💡
React 18 a 19 — Dnes používejte React 18+. React 19 (2024) přinesl Server Components, Actions, a use() hook. V tomto kurzu pokryjeme React 18 základ + nejdůležitější React 19 novinky.
// 01 / 09 JSX →

JSX — JavaScript + XML

JSX je syntaktické rozšíření JavaScriptu — vypadá jako HTML, ale je to JS. Babel ho transpiluje na React.createElement() volání. Není povinný, ale prakticky vždy se používá.

// JSX se transpiluje na React.createElement()
const element = <h1 className="title">Ahoj světe</h1>;

// Je ekvivalentní s:
const element = React.createElement(
  'h1',
  { className: 'title' },
  'Ahoj světe'
);

// JSX pravidla:
// 1. Jeden root element (nebo React.Fragment / <></>)
// 2. class → className, for → htmlFor
// 3. Výrazy v {} — lze použít JS
// 4. CamelCase pro HTML atributy (onClick, onChange)
// 5. Samouzavírací tagy: <br />, <img />, <Input />
// JSX výrazy — JavaScript uvnitř {}
const jmeno = "Jana";
const cislo = 42;
const pole  = ["a", "b", "c"];

const element = (
  <>
    <h1>Ahoj, {jmeno}!</h1>
    <p>Číslo: {cislo * 2}</p>
    <p>Datum: {new Date().getFullYear()}</p>
    <p>{cislo > 10 ? "Velké" : "Malé"}</p>
    <ul>{pole.map(x => <li key={x}>{x}</li>)}</ul>

    {/* Komentáře takto */}
    {podminka && <span>Jen pokud true</span>}
  </>
);
⚠️
JSX nelze spustit přímo v prohlížeči — musí ho zpracovat Babel (součást Vite/CRA). Editory níže simulují React pomocí Babel CDN loadovaného v iframe, takže JSX skutečně funguje.
App.jsx — JSX výrazy
// 02 / 09 Komponenty →

Komponenty

Komponenta je funkce, která přijme props a vrátí JSX. React aplikace je strom komponent — každá zodpovídá za část UI.

// Funkční komponenta — základní forma
function Button({ label, onClick, variant = "primary" }) {
  return (
    <button
      onClick={onClick}
      className={`btn btn-${variant}`}
    >
      {label}
    </button>
  );
}

// Pojmenování: vždy PascalCase (Button, ne button)
// Proč? React rozlišuje lowercase (nativní HTML) od PascalCase (komponenty)

// Kompozice — komponenty uvnitř komponent
function Card({ title, children }) {
  return (
    <div className="card">
      <h3>{title}</h3>
      <div className="card-body">{children}</div>
    </div>
  );
}

// Použití:
<Card title="Moje karta">
  <p>Libovolný obsah jako children</p>
  <Button label="Klikni" onClick={() => alert('!')} />
</Card>
Kompozice komponent

// Kvíz: Proč musí být název React komponenty PascalCase (velké první písmeno)?

// 03 / 09 Props →

Props — data shora dolů

Props (properties) jsou read-only data předaná z rodiče do dítěte. Data tečou jedním směrem — shora dolů. Dítě nikdy nemění props rodiče (one-way data flow).

// Props jsou read-only!
function Component({ name }) {
  // name = "Jan"; // ❌ CHYBA — props nelze měnit
  return <h1>{name}</h1>;
}

// Destrukturování props (doporučeno)
function Button({ label, onClick, disabled = false, size = "md" }) { }

// Celý objekt props (méně časté)
function Button(props) {
  return <button onClick={props.onClick}>{props.label}</button>;
}

// Spread props — přeposlání dál
function Input({ label, ...rest }) {
  return (
    <div>
      <label>{label}</label>
      <input {...rest} />  {/* předá všechny ostatní props */}
    </div>
  );
}

// children — speciální prop pro vnoření
function Card({ title, children }) {
  return (
    <div>
      <h2>{title}</h2>
      {children}
    </div>
  );
}

// Validace props s TypeScript
interface ButtonProps {
  label: string;
  onClick?: () => void;
  variant?: "primary" | "secondary" | "danger";
  disabled?: boolean;
}
// 04 / 09 useState →

useState — reaktivní stav

useState je nejzákladnější React hook. Když se stav změní, React automaticky re-renderuje komponentu s novou hodnotou.

import { useState } from 'react';

function Counter() {
  // [aktuální hodnota, funkce pro změnu]
  const [count, setCount] = useState(0); // 0 = výchozí hodnota

  return (
    <div>
      <p>Počet: {count}</p>
      <button onClick={() => setCount(count + 1)}>+</button>
      <button onClick={() => setCount(count - 1)}>-</button>
      <button onClick={() => setCount(0)}>Reset</button>
    </div>
  );
}

// ✅ Funkční update — pro stav závislý na předchozím
setCount(prev => prev + 1); // bezpečnější v async kontextech

// ✅ Objekt jako stav
const [user, setUser] = useState({ name: 'Jana', age: 30 });
// Vždy merge ručně! setState PŘEPÍŠE celý objekt
setUser(prev => ({ ...prev, age: 31 }));

// ✅ Pole jako stav
const [items, setItems] = useState([]);
// Přidání
setItems(prev => [...prev, novyItem]);
// Smazání
setItems(prev => prev.filter(item => item.id !== id));
// Aktualizace
setItems(prev => prev.map(item =>
  item.id === id ? { ...item, done: true } : item
));
useState — counter + input

// Kvíz: Máte useState({ name: 'Jana', age: 30 }). Chcete aktualizovat pouze age. Jak?

// 05 / 09 Events →

Event handlers

// Předáváme funkci, ne volání!
<button onClick={handleClick}>        {/* ✅ */}
<button onClick={handleClick()}>      {/* ❌ zavolá se ihned! */}
<button onClick={() => handleClick()}> {/* ✅ arrow funkce */}

// Syntetické eventy — React wrapper nad nativními
function Form() {
  const handleSubmit = (e) => {
    e.preventDefault(); // zabrání reload stránky
    const data = new FormData(e.target);
    console.log(Object.fromEntries(data));
  };

  const handleChange = (e) => {
    console.log(e.target.name, e.target.value);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="email" type="email" onChange={handleChange} />
      <button type="submit">Odeslat</button>
    </form>
  );
}

// Předání dat do handleru
{items.map(item => (
  <button key={item.id} onClick={() => handleDelete(item.id)}>
    Smazat {item.name}
  </button>
))}
// 06 / 09 Listy & keys →

Listy a key prop

Renderování pole v React probíhá přes .map(). Každý element musí mít unikátní key — React ho používá při reconciliation k identifikaci položek.

const produkty = [
  { id: 1, nazev: "Nůž A", cena: 299 },
  { id: 2, nazev: "Nůž B", cena: 499 },
];

// ✅ Správně — key z unikátního ID
{produkty.map(p => (
  <ProductCard key={p.id} {...p} />
))}

// ❌ Index jako key — problém při mazání/přesouvání
{produkty.map((p, index) => (
  <ProductCard key={index} {...p} />  // NEPOUŽÍVAT pokud lze
))}

// Filtrování a řazení
const filtered = produkty
  .filter(p => p.cena < 400)
  .sort((a, b) => a.nazev.localeCompare(b.nazev));

// Podmíněné renderování
{isLoading && <Spinner />}
{error && <ErrorMessage error={error} />}
{!isLoading && !error && <ProductList items={data} />}
Dynamický seznam s filtrem

// Kvíz: Proč je index jako key v listu problematický?

// 07 / 09 Cvičení →

Cvičení — Todo aplikace

Napište kompletní Todo aplikaci: přidávání úkolů (input + Enter), označení jako hotovo (kliknutí), smazání, počítadlo zbývajících, filtr (vše/aktivní/hotové).

📋
Checklist: useState([]) pro seznam, každý todo = { id, text, done }, unikátní ID (Date.now()), .map() pro render, .filter() pro filtraci, immutabilní update (spread).
Todo App — vaše řešení
🏅

React Foundations

Zvládáte JSX, komponenty, props, useState a renderování listů.

// 08 / 09 Taháček →

// Taháček

Komponenta skeleton

// Funkční komponenta s TypeScript
interface Props {
  title: string;
  count?: number;
  onAction: () => void;
}

export function MojeKomponenta({ title, count = 0, onAction }: Props) {
  const [state, setState] = React.useState(false);

  return (
    <div>
      <h2>{title}</h2>
      <p>{count}</p>
      <button onClick={onAction}>Akce</button>
    </div>
  );
}

useState patterns

// Primitivní hodnota
const [val, setVal] = useState(0);

// Objekt — vždy spread!
const [obj, setObj] = useState({ a: 1, b: 2 });
setObj(prev => ({ ...prev, b: 3 }));

// Pole — immutabilní operace
const [arr, setArr] = useState([]);
setArr(prev => [...prev, novyItem]);         // přidat
setArr(prev => prev.filter(x => x.id !== id)); // smazat
setArr(prev => prev.map(x => x.id === id ? {...x, done:true} : x)); // update
// Lekce 1 dokončena Lekce 2: React Hooks →