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

JavaScript obecně

  • 01 Proměnné, typy a operátory0/2
  • 02 Funkce a closures0/2
  • 03 Objekty0/2
  • 04 Pole I: map, filter a spol.0/2
  • 05 Pole II: reduce, řazení a seskupování0/3
  • 06 Podmínky a logické operátory0/2
  • 07 Asynchronní JavaScript0/2
  • 08 Moduly a vestavěné nástroje0/2

JavaScript pro React

  • 09 Reference a immutabilita0/3
  • 10 JS vzory, které v Reactu potkáš všude0/2
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ů

JavaScript 09 · JavaScript pro React

Reference a immutabilita

Hodnota vs. odkaz, mělká kopie, vnořené úpravy bez mutací – a proč na tom React stojí.

Načítám lekci…

💬 Našel jsi chybu, něco ti není jasné, nebo máš nápad? Napiš mi.
← Moduly a vestavěné nástrojeJS vzory, které v Reactu potkáš všude →

Než začneme: proč tahle lekce patří k Reactu

Předchozí lekce byly obecný JavaScript. Tahle a příští jsou o JavaScriptu, který React vyžaduje. Kdo je přeskočí, potká v Reactu záhadné chyby: „změnil jsem data, ale stránka se nepřekreslila“ nebo „změnil jsem jednu věc a rozbila se jiná“.

Obě chyby mají stejnou příčinu: nepochopení toho, jak JavaScript zachází s objekty a poli. Dvojčata vypadají stejně, ale jsou to dva různí lidé. A jeden člověk může mít dvě přezdívky, ale pořád je to jeden člověk. Přesně v tomhle se JavaScript s objekty liší od čísel a textů.

ℹ️ Co tě v lekci čeká
  1. Hodnota vs. odkaz: cedulka s adresou.
  2. Porovnávání objektů přes ===.
  3. Mutace: když funkce mění, co jí nepatří.
  4. Mělká a hluboká kopie.
  5. Kuchařka neměnných úprav pro objekty a pole.
  6. Proč na tom React stojí.

1. Hodnota vs. odkaz

Primitivní hodnoty (čísla, texty, booleany) se při přiřazení zkopírují. Každá proměnná má svou vlastní:

let a = 5
let b = a // b dostane KOPII hodnoty 5
b = 10
a // 5 – a se nezměnilo

Objekty a pole se chovají jinak. Proměnná v sobě nemá samotný objekt, ale cedulku s adresou, kde objekt bydlí. Přiřazení zkopíruje jen cedulku – obě proměnné pak vedou do stejného domu:

const a = { count: 1 }
const b = a // b NENÍ kopie – je to druhá cedulka se STEJNOU adresou
b.count = 99
a.count // 99 😱 změnil se i „a“ – je to tentýž objekt
const list = [1, 2]
const sameList = list
sameList.push(3)
list // [1, 2, 3]
Primitivní hodnotyObjekty, pole, funkce
Příklady5, 'ahoj', true, null{ … }, [ … ], () => …
Přiřazení b = azkopíruje hodnotuzkopíruje odkaz (adresu)
a === b porovnáhodnotujestli je to TENTÝŽ objekt
Jdou změnit „uvnitř“?ne (5 je prostě 5)ano (vlastnosti, položky)

2. Porovnávání objektů

'ahoj' === 'ahoj' // true – stejná hodnota
[1, 2] === [1, 2] // false – dvě různá pole (dvě adresy), jen vypadají stejně
{ a: 1 } === { a: 1 } // false
const x = { a: 1 }
const y = x
x === y // true – stejná adresa
// Porovnání podle obsahu si musíš napsat sám
x.a === y.a
JSON.stringify(arr1) === JSON.stringify(arr2) // rychlá (ale nedokonalá) kontrola obsahu
❓ Proč JavaScript neporovnává obsah?

Protože by to bylo pomalé: u velkého vnořeného objektu by musel projít každou vlastnost. Porovnání adres je okamžité. A právě tuhle rychlost využívá React – nemusí procházet celý stav, stačí mu zjistit, jestli dostal nový objekt.

3. Mutace: když funkce mění, co jí nepatří

Mutace znamená změnit existující objekt nebo pole „uvnitř“ – přepsat vlastnost, přidat položku, seřadit na místě. Problém nastane, když ten objekt sdílí víc míst v kódu:

function sortByPrice(products) {
return products.sort((a, b) => a.price - b.price) // ❌ sort mění předané pole
}
const catalog = loadProducts() // pořadí podle katalogu
const cheapFirst = sortByPrice(catalog)
// 😱 catalog je teď taky seřazený podle ceny – funkce mu ho přeházela
function sortByPrice(products) {
return products.toSorted((a, b) => a.price - b.price) // ✅ vrátí nové pole
}
❌ Mutuje✅ Vyrábí nové
obj.name = 'x'{ ...obj, name: 'x' }
delete obj.nameconst { name, ...rest } = obj
arr.push(x)[...arr, x]
arr.splice(i, 1)arr.filter(…) / arr.toSpliced(i, 1)
arr[i] = xarr.map(…) / arr.with(i, x)
arr.sort()arr.toSorted()
arr.reverse()arr.toReversed()
Ukázka

Push vs. spread

Co vidíš: Dvě tlačítka, dva způsoby, jak získat pole se švestkou navíc. Pod nimi výpis obou polí.

Vyzkoušej:

  1. Klikni na Zkopírovat proměnnou a push. Švestka přibyla i do původního fruits a next === fruits je true.
  2. Klikni na Nové pole přes spread. Původní pole zůstalo, next === fruits je false.

Co z toho plyne: Přiřazení nekopíruje. Nové pole (nebo objekt) vznikne jen ze spreadu, map, filter a podobných metod – a jen takovou změnu React pozná.

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

4. Mělká a hluboká kopie

Spread vyrobí mělkou kopii: nový objekt na první úrovni, ale vnořené objekty jsou pořád tytéž. Jako když okopíruješ seznam adres – papír je nový, ale domy na adresách stejné.

const user = { name: 'Jana', address: { city: 'Praha' } }
const copy = { ...user }
copy === user // false – nový objekt
copy.address === user.address // true – vnořený objekt je SDÍLENÝ
copy.name = 'Eva' // user.name zůstane 'Jana' ✅
copy.address.city = 'Brno' // user.address.city je taky 'Brno' 😱

Hluboká kopie

const deep = structuredClone(user) // zkopíruje všechno do hloubky
deep.address === user.address // false
// Starší trik (ztratí Date, funkce, undefined):
const deep2 = JSON.parse(JSON.stringify(user))
✨ V Reactu většinou nechceš hlubokou kopii
Kopírovat celý stav při každé změně je zbytečné a pomalé. A navíc to rozbije optimalizace: React (a memo) by si myslel, že se změnilo úplně všechno. Správná cesta je zkopírovat jen to, co leží na cestě ke změně – viz kuchařka níže.

5. Kuchařka neměnných úprav

Tyhle vzory budeš v Reactu psát pořád dokola – při každé změně stavu. Vyplatí se je mít v ruce.

Objekty

const user = { name: 'Jana', age: 31, address: { city: 'Praha', zip: '11000' } }
{ ...user, age: 32 } // změnit vlastnost
{ ...user, email: 'jana@example.cz' } // přidat vlastnost
const { age, ...withoutAge } = user // odebrat vlastnost
{ ...user, [field]: value } // změnit vlastnost podle proměnné
{ ...user, address: { ...user.address, city: 'Brno' } } // změnit vnořenou – spread na každé úrovni
{ ...user, age: user.age + 1 } // odvodit z předchozí hodnoty

Pole

const todos = [
{ id: 1, title: 'Nakoupit', done: false },
{ id: 2, title: 'Uklidit', done: true },
]
[...todos, newTodo] // přidat na konec
[newTodo, ...todos] // přidat na začátek
todos.toSpliced(1, 0, newTodo) // vložit na pozici 1
todos.filter((t) => t.id !== id) // odebrat podle id
todos.map((t) => (t.id === id ? { ...t, done: !t.done } : t)) // změnit jednu položku
todos.map((t) => (t.id === id ? { ...t, title: newTitle } : t)) // přejmenovat jednu
todos.map((t) => ({ ...t, done: true })) // změnit všechny
todos.filter((t) => !t.done) // odebrat všechny hotové
todos.toSorted((a, b) => a.title.localeCompare(b.title)) // seřadit
⚠️ map: neměněné položky vracej beze změny
todos.map((t) => (t.id === id ? { ...t, done: !t.done } : t)) // ✅ ostatní jsou TYTÉŽ objekty
todos.map((t) => (t.id === id ? { ...t, done: !t.done } : { ...t })) // ⚠️ zbytečně kopíruje všechny
todos.map((t) => { if (t.id === id) t.done = !t.done; return t }) // ❌ mutuje původní objekt!

Pole uvnitř objektu (a naopak)

const board = {
title: 'Projekt',
columns: [
{ id: 'todo', cards: [{ id: 1, text: 'A' }] },
{ id: 'done', cards: [] },
],
}
// Přidat kartu do sloupce 'done'
const next = {
...board, // 1. nový board
columns: board.columns.map((col) => // 2. nové pole sloupců
col.id === 'done'
? { ...col, cards: [...col.cards, newCard] } // 3. nový sloupec s novým polem karet
: col, // ostatní sloupce beze změny
),
}

Postup je pořád stejný: jdi po cestě ke změně a na každé úrovni vyrob kopii; všechno mimo cestu nech, jak je. Když je zanoření moc hluboké, je to signál, že by stav šel uspořádat ploššeji (lekce React 05 a 15).

6. Proč na tom React stojí

Když v Reactu změníš stav, React porovná starou a novou hodnotu přes === (přesněji Object.is). Když je to tentýž objekt, usoudí, že se nic nezměnilo, a komponentu nepřekreslí.

// Ukázka z lekce React 02 – stav s polem úkolů
const [todos, setTodos] = useState([])
// ❌ Mutace: pole je pořád TOTÉŽ, React nic nepozná, stránka se nezmění
todos.push(newTodo)
setTodos(todos)
// ✅ Nové pole: React vidí jinou adresu → překreslí
setTodos([...todos, newTodo])
  • Rychlá detekce změn: stačí porovnat adresu, ne procházet obsah.
  • Optimalizace: memo, useMemo a závislosti efektů fungují jen díky tomu, že nezměněná data mají stejnou adresu a změněná novou.
  • Předvídatelnost: každý render pracuje se svým „snímkem“ dat, který mu nikdo pod rukama nezmění.
  • Undo/redo zadarmo: staré verze stavu zůstávají netknuté – stačí si je schovat (lekce React 05).
✨ Object.freeze – pojistka při učení
Object.freeze(obj) zakáže změny objektu. Ve striktním režimu (moduly ho mají vždy) pak každý pokus o mutaci vyhodí chybu. Výzvy v kurzu tak hlídají, že nic neměníš. Pozor, freeze je také mělký – vnořené objekty musíš zmrazit zvlášť.

Ověř si to

Kvíz const b = a; b.push(3) – co se stane s polem a?
Kvíz Co vrátí { a: 1 } === { a: 1 }?
Kvíz Jak správně změníš done u úkolu s id 2 v poli todos?
Kvíz const copy = [...todos]; copy[0].done = true. Změní se todos[0].done?

Rozcvička

Výzva

Nové místo změněného

Rozcvička

Přidej položku do pole a přejmenuj uživatele – pokaždé tak, že vznikne nové pole nebo nový objekt.

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…

Výzvy

První výzva procvičí základní neměnné úpravy na jednoduchých datech. Druhá jde do hloubky: stav s projekty a úkoly, kde testy přes === kontrolují, že kopíruješ jen to nutné – přesně jako React.

Výzva

Bez mutací

Lehká

Šest malých funkcí, které vracejí nová data místo úprav starých. Přesně takové funkce budeš v dalších lekcích psát pro změnu stavu. Co přesně má která vracet, 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…

Výzva

Vnořené úpravy

TěžkáBonus · zdarma po přihlášení

Stav aplikace s uživatelem, projekty a úkoly. Napiš šest funkcí, které v něm změní jednu věc – a všechno ostatní nechají jako tytéž objekty. Celý stav i to, co má která funkce změnit, je v komentářích v kódu.

🔓 Bonusová výzva – zdarma po přihlášení

Střední a těžké výzvy se odemknou, jakmile se přihlásíš. Stejně jako celý kurz jsou zdarma – stačí e-mail, žádné heslo ani platba.

Jak nakládáme s tvými údaji

Shrnutí

  • Primitivní hodnoty se kopírují, objekty a pole sdílejí. Proměnná s objektem je cedulka s adresou.
  • === u objektů porovnává adresu, ne obsah. Dva stejně vypadající objekty nejsou „stejné“.
  • Mutace mění objekt pro všechny, kdo na něj odkazují. push, splice, sort a přiřazení do vlastnosti mutují.
  • Spread je mělký. U vnořených dat kopíruj každou úroveň na cestě ke změně, zbytek nech.
  • Kuchařka: přidat = spread, odebrat = filter, změnit jednu položku = map s ternárem, změnit vlastnost = { ...obj, key: value }.
  • React pozná změnu jen podle nové adresy. Mutovaný stav = nepřekreslená stránka.