Skip to content

Commit

Permalink
Merge pull request #667 from Czechitas-podklady-WEB/666-react-4-aktua…
Browse files Browse the repository at this point in the history
…lizace-textu-lekce

React 4: Aktualizace textu lekce
  • Loading branch information
podlomar authored Nov 5, 2023
2 parents e9e6cf0 + e69fefc commit fa681ff
Show file tree
Hide file tree
Showing 14 changed files with 211 additions and 87 deletions.
2 changes: 1 addition & 1 deletion daweb/react/entry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ lessons:
- uvod-do-reactu
- formulare-efekty
- komunikace-dite-rodic
- komunikace-sourozenci
- pokrocila-komunikace
- react-router
- leviexpress-1
- leviexpress-2
Expand Down
4 changes: 3 additions & 1 deletion daweb/react/komunikace-dite-rodic/shrnuti.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ Po této lekci byste měli vědět a znát

- základní povědomí, jak probíhá komunikace mezi komponentami,
- jak komponenty komunikují pomocí _props_, tedy ve směru od rodiče k dítěti,
- jak komponenty komunikují pomocí callbacků, tedy ve směru od dítěte k rodiči.
- jak komponenty komunikují pomocí callbacků, tedy ve směru od dítěte k rodiči,
- hlavní poučku pro jednoduchou komunikaci od dítěte k rodiči:
> Dítě může rodiči poslat informaci tak, že nastaví jeho stav. Rodič tak musí dítěti předat funkci, která mu umožní stav měnit.
6 changes: 0 additions & 6 deletions daweb/react/komunikace-sourozenci/entry.yml

This file was deleted.

22 changes: 0 additions & 22 deletions daweb/react/komunikace-sourozenci/excs/produkty.md

This file was deleted.

57 changes: 0 additions & 57 deletions daweb/react/komunikace-sourozenci/sourozenci.md

This file was deleted.

70 changes: 70 additions & 0 deletions daweb/react/pokrocila-komunikace/dite-rodic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
## Pokročilejší komunikace od dítěte k rodiči

V předchozí lekci jsme se naučili jednoduchou komunikaci od dítěte k rodiči, která by se dala shrnout takto:

> Dítě může rodiči poslat informaci tak, že nastaví jeho stav. Rodič tak musí dítěti předat funkci, která mu umožní stav měnit.
My jsme proto dítěti vždycky posílali přímo funkci `setState`, tedy například `setPresident` v naší ukázce s volbami. Snadno ovšem narazíme na situace, kdy si s tímto postupem nevystačíme. Většinou to je ve chvíli, kdy chceme rodiči od dítěte předat nějakou informaci a rodič se má sám rozhodnout, jak s touto informací naloží. Zda například změní stav, jakým zůsobem jej změní nebo třeba udělá něco úplně jiného, například odešle data na server, zavolá nějaký API endpoint apod.

Pro názornou ukázku s vraťme k našemu příkladu s volbami. V minulé lekci jsme si ukázali, jak může komponenta `Candidate` poslat informaci o tom, kdo byl zvolen, svému rodiči `HomePage`. Tato komunikace proběha srkze funkci předanou skrze prop `onVote`.

```jsx
export const Candidate = ({ name, avatar, onVote }) => {
const handleClick = () => {
onVote(name);
};

return (
<div className="candidate">
<h3 className="candidate__name">{name}</h3>
<img className="candidate__avatar" src={avatar} />
<button onClick={handleClick} className="btn-vote">
Vybrat
</button>
</div>
);
};
```

Této komponentě pak její rodič `HomePage` předával rovnou funkci `setPresident`, která nastaví stav `president` na jméno kandidáta.

```jsx
<div className="candidate-list">
{candidates.map((c) => (
<Candidate
key={c.name}
name={c.name}
avatar={c.avatar}
onVote={setPresident}
/>
))}
</div>
```

Co kdybychom však chtěli, aby se po kliknutí na kandidáta zobrazilo nejdříve nějaké upozornění a až po jeho potvrzení se změnil stav `president`? Nebo bychom zároveň se změnou stavu chtěli odeslat na server informaci o tom, kdo byl zvolen?

V takovém případě musíme udělat krok navíc a v komponentě `HomePage` vytvořit novou funkci `handleVote`, která bude obsahovat veškerou logiku, která se má provést po kliknutí na kandidáta. Tato funkce pak bude předána komponentě `Candidate` skrze prop `onVote`.

```jsx
const handleVote = (name) => {
if (window.confirm(`Opravdu chcete zvolit kandidáta ${name}?`)) {
setPresident(name);
// zde bychom mohli odeslat data na server
}
};
```

```jsx
<div className="candidate-list">
{candidates.map((c) => (
<Candidate
key={c.name}
name={c.name}
avatar={c.avatar}
onVote={handleVote}
/>
))}
</div>
```

Komponenta `Candidate` se tedy chová stejně, jako prve, ale v `HomePage` máme o krok navíc.
9 changes: 9 additions & 0 deletions daweb/react/pokrocila-komunikace/entry.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
title: Pokročilá komunikace
lead: Náročnejší typy komunikace mezi komponentami a jak se s nimi vypořádat
access: 'claim'
sections:
- dite-rodic
- sourozenci
- potomci
- shrnuti
# - cv-sourozenci
61 changes: 61 additions & 0 deletions daweb/react/pokrocila-komunikace/potomci.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
## Komunikace mezi potomky

Náš příklad s volbami by ještě pořád mohl být o kus složitější. V reálných aplikacích se totiž setkáme s tím, že komponenty jsou do sebe zanořeny o několik úrovní hlouběji. V takovém případě se může stát, že komponenta, která potřebuje nějaká data, je od komponenty, která tato data má, vzdálena o několik svých předků. Pokud takovéto komponenty spolu potřebují komunikovat, mluvíme o komunikaici mezi potomky.

V takové situaci másíme data předávat přes několik komponent, které tato data nepotřebují, pouze je předávají dále. Tento postup se běžně nazývá _prop drilling_, česky bychom mohli říct _vrtání_.

Pro názorný příklad si představme, že bychom v naší aplikaci s volbami měli ještě další komponentu `CandidateList`, která by zobrazovala seznam všech kandidátů. To znamená, že komponenta `Candidate` je zanořena o jednu úrovně hlouběji a musíme k ní funkci `handleVote` provrtat přes komponentu `CandidateList`.

```jsx
const CandidateList = ({ candidates, onVote }) => {
return (
<div className="candidate-list">
{candidates.map((c) => (
<Candidate
key={c.name}
name={c.name}
avatar={c.avatar}
onVote={onVote}
/>
))}
</div>
);
};
```

JSX v komponentě `HomePage` pak vypadá takto:

```jsx
return (
<div className="container">
<Castle president={president} />
<h2>Kandidátí</h2>
<CandidateList candidates={candidates} onVote={handleVote} />
</div>
);
```

Kdybychom si chtěli život ještě o kousek ztížit, mohli bychom si představit, že tlačítko pro volbu kandidáta je také nějaká složitější komponenta, například tlačítko s ikonou. V takovém případě bychom museli funkci `handleVote` provrtat ještě o jednu úroveň hlouběji.

```jsx
const Candidate = ({ name, avatar, onVote }) => {
return (
<div className="candidate">
<h3 className="candidate__name">{name}</h3>
<img className="candidate__avatar" src={avatar} />
<VoteButton onVote={onVote} />
</div>
);
};
```

## Prop drilling v praxi

Prop driling nám slouží k tomu, aby spolu mohly komunikovat komponenty, které jsou od sebe vzdáleny o několik úrovní hlouběji. Tato technika má však také své nevýhody. Tak hluboké vrtání, jako jsme vidělí výše, je ještě v praxi únosné, ale hlubší prop driling začiná mít zásadní nevýhody:

1. **Horší přehlednost** – čtenář kódu musí složitě navigovat kódem a držet v hlavě, kudy všude proudí data.
1. **Nepohodlnost** – psát kód s hlubokým vrtáním je pro programátory otravné a zdlouhavé.
1. **Průchozí komponenty** – máme v kódu spoustu komponent, která data ve skutečnosti nepotřebují, jen je předávají dále, čímž se komplikuje jejich kód.
1. **Horší udržitelnost** – pokud se rozhodneme změnit strukturu komponent, musíme přepisovat i průchozí komponenty, která data vlastně nepotřebují.

V reálnách aplikacích se proto příliš agresivnímu prop drilingu snažíme vyhnout. Pokud to z hlediska struktury aplikace dává smysl, snažíme se předávání dat mezi komponentami řešit jinými způsoby, například pomocí _contextu_ nebo pomocí _state managementu_. To jsou však pro tento kurz příliš pokročilá témata. V následujících lekcích se proto omezíme na vrtání maximálně přes jednu komponentu, nebo se mu budeme vyhýbat úplně.
8 changes: 8 additions & 0 deletions daweb/react/pokrocila-komunikace/shrnuti.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
## Shrnutí

Po této lekci byste měli vědět a znát

- jak zařídit pokročilejší komunikaci od dítěte k rodiči,
- jak funguje komunikace mezi sourozenci,
- k čemá slouží prop drillling a jak jej použít pro komunikaci mezi potomky,
- jaké jsou nevýhody a úskalí hlubokého prop drillingu a proč se mu snažíme vyhnout.
59 changes: 59 additions & 0 deletions daweb/react/pokrocila-komunikace/sourozenci.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
## Komunikace mezi sourozenci

Komunikaci mezi sourozenci je potřeba ve chvíli, kdy máme si potřebují nějaká data vyměnit komponenty, které mají společného rodiče.

Tuto situaci si opět ilustrujeme na našem příkladu s volbou nového prezidenta. V minulé lekci jsme skončili v situaci, kdy komponenta `Candidate` dokáže svému rodiči `HomePage` poslat jméno kandidáta pomocí callbacku `onVote`. Naše aplikace však byla napsaná stále dosti jednoduše. V realitě bychom nejspíše narazili na složitější situaci. Podívejme se na začátek komponenty `HomePage`.

```js
return (
<div className="container">
<div className="castle">
<div className="castle__image"></div>
<div className="castle__body">
<h1>Nový prezident</h1>
<p className="castle__president">
{
president === null ? 'Vyberte svého kandidáta' : president
}
</p>
</div>
</div>
```
Tento úsek představující náš hrad by si jistě zasloužil samostatnou komponentu. Pojmenujme ji `Castle` a vytvoříme pro ni separátní složku i styly. Kód komponenty pak bude vypadat takto.
```js
import React from 'react';
import './style.css';

export const Castle = ({ president }) => {
return (
<div className="castle">
<div className="castle__image"></div>
<div className="castle__body">
<h1>Nový prezident</h1>
<p className="castle__president">
{president === null ? 'Vyberte svého kandidáta' : president}
</p>
</div>
</div>
);
};
```
Naše komponenta `HomePage` pak může komponentu `Castle` použíjt jako svoje dítě.
```jsx
return (
<div className="container">
<Castle president={president} />

<h2>Kandidátí</h2>
);
```
Všimněte si, že komponenta `Candidate` a komponenta `Castle` jsou sourozenci. Jejich společným rodičem je komponenta `HomePage`. Komponenta `Candidate` už neposílá jméno kandidáta svému rodiči jako dříve. Nyní jej posílá svému sourozenci `Castle`. Všimněte si však, že tato komunikace probíhá skrze rodiče `HomePage`. Jakmile komponenta `Candidate` zavolá callback `handleVote`, tento uloží jméno prezidenta do stavu komponenty `App`. Tímto se vyvolá překreslení komponenty `Home`, čimž se hodnota stavu `president` předá do props komponenty `Castle`.
Z této ukázky plyne pravidlo pro komunikaci mezi sourozenci:
> Komponenta může poslat informaci svému sourozenci tak, že změní stav svého rodiče. Tento stav pak bude předán jako props jinému dítěti.

0 comments on commit fa681ff

Please sign in to comment.