Lekce 5 - Stavy v Reactu a hook useState()
V minulé lekci, Vlastnosti v React - Props, jsme si vysvětlili koncept vlastností props. Ukázali jsme si, jak se vlastnosti zadávají v HTML kódu, načítají v komponentě a destrukturalizují.
V tomto React tutoriálu se naučíme obsluhovat stavy naší aplikace,
objevíme React hooks a naučíme se používat hook
useState()
.
Stavy v React a hooks
Stav React komponent (state) je podobný vlastnostem, ale používá se pouze uvnitř komponenty a může se měnit. V podstatě se jedná o privátní data komponenty pouze pro vnitřní použití.
Stavy jsou jinými slovy proměnné, které se v React aplikaci mění a my na jejich změnu chceme nějak reagovat.
Hooks
Jak jsme již nastínili v úvodní lekci, k funkcionalitám Reactu se
dostáváme pomocí hooks. Hook (v překladu háček) není nic jiného, než
funkce, která nám zpřístupní nějakou funkcionalitu z
React. "Zasekneme" se háčkem do Reactu a "taháme" si, co
potřebujeme. Pro práci s vnitřním stavem komponenty budeme používat hook
(funkci) useState()
.
Práce s hookem useState()
Použití useState()
probíhá ve třech krocích:
1. Hook musíme prvně naimportovat, abychom měli funkci
useState()
dostupnou:
import React, { useState } from 'react';
2. Zavoláme useState()
a předáme mu výchozí
hodnotu stavu (ta může být i null
):
useState(null);
3. Funkce useState()
nám vrátí pole se
dvěma proměnnými, kde první proměnná obsahuje současný
stav a druhá proměnná je funkce pro změnu tohoto
stavu:
const [stav, setStav] = useState(null);
Proměnná stav
nyní obsahuje stav s hodnotou
null
. A pomocí funkce setStav()
tento stav můžeme
měnit. Stejným způsobem si můžeme v komponentě vytvořit další stavy.
Pojďme si tento jednoduchý mechanismus vyzkoušet na příkladu.
Vytvoření projektu - Tlačítko s poštovní schránkou
Pro vyzkoušení stavů si vytvoříme projekt, ve kterém bude tlačítko s poštovní schránkou. Jakmile na něj klikneme, schránka se otevře a po opětovném kliknutí se zase zavře. K reprezentaci stavu schránky použijeme stav.
V průzkumníku Windows klikneme pravým tlačítkem myši na naši složku s projekty za držení klávesy Shift. V kontextovém menu vybereme otevření nového okna Terminal/PowerShell:
Pomocí následujícího příkazu vytvoříme nový React projekt s názvem
mailbox
:
Windows PowerShell PS C:\Users\sdrac\Dropbox> npx create-react-app@^5.0.1 mailbox
Pomocí příkazu cd
se přesuneme do složky projektu:
Windows PowerShell PS C:\Users\sdrac\Dropbox> cd mailbox
A projekt spustíme příkazem:
Windows PowerShell PS C:\Users\sdrac\Dropbox\mailbox> npm start
Následně se přesuneme opět do Průzkumníku Windows, klikneme na složku
mailbox/
pravým tlačítkem myši a otevřeme ji ve VS Code
pomocí nabídky Otevřít v Code.
Komponenta MailboxButton
Ve složce src/
si vytvoříme nový soubor
MailboxButton.js
. Do něj vložíme následující základ
komponenty:
function MailboxButton() { return <button>📪</button>; } export default MailboxButton;
Soubor uložíme. Pro obrázek poštovní schránky jsme
použili emoji, abychom jej nemuseli stahovat 🤩 Trochu si jej ještě
zvětšíme přidáním CSS stylu do App.css
:
button { margin-top: 5rem; font-size: 10rem; }
Soubor opět uložíme.
Vložení komponenty do
App.js
Z komponenty App.js
odstraníme nepotřebné součásti
výchozího React projektu. Dále naimportujeme naši komponentu
MailboxButton
a vložíme ji do HTML kódu komponenty
App
:
import './App.css'; import MailboxButton from './MailboxButton'; function App() { return ( <div className="App"> <MailboxButton /> </div> ); } export default App;
Soubor uložíme a můžeme se podívat do prohlížeče, kde uvidíme naše tlačítko:
Přidání stavu
Do komponenty si nyní naimportujeme hook useState()
a jeho
zavoláním si definujeme stav pro to, zda je poštovní schránka plná:
import React, { useState } from 'react'; function MailboxButton() { const [full, setFull] = useState(false); // vytvoření stavu full return ( <button> {full ? '📬' : '📪'} </button> ); } export default MailboxButton;
Pomocí funkce useState()
jsme vytvořili dvě proměnné:
full
- Stav určující, zda je poštovní schránka plná.setFull()
- Setter funkce pro změnu stavu poštovní schránky.
Při volání useState()
jsme zároveň nastavili, že výchozí
stav je false
.
Rovněž jsme upravili vykreslení tlačítka. To je nyní rozepsané na
více řádek, což způsobilo, že jsme celé JSX museli vložit do závorek
()
. Do tlačítka vykreslujeme buď emoji plné poštovní
schránky 📬 nebo prázdné 📪 podle toho, zda je stav full
true
nebo false
. Použili jsme ternární operátor v
JSX, kdy se při pravdivé hodnotě proměnné vykreslí část za otazníkem
?
a při nepravdivé část za dvojtečkou :
. Určitě
jej znáte i z JavaScriptu.
V prohlížeči si můžeme zkontrolovat, že aplikace dělá stále to
samé. Můžete si zkusit změnit výchozí stav v parametru funkce
useState()
z false
na true
, čímž se
změní obrázek na schránce:
Změna stavu
Nyní elementu <button>
obsloužíme událost
onClick
. K tomu si vytvoříme funkci uvnitř komponenty s názvem
začínajícím na handle*
, v našem případě
handleClick()
:
import React, { useState } from 'react'; function MailboxButton() { const [full, setFull] = useState(false); function handleClick() { setFull(!full); } return ( <button onClick={handleClick}> {full ? '📬' : '📪'} </button> ); } export default MailboxButton;
Ve funkci nastavíme pomocí setteru setFull()
novou hodnotu
stavu a to na negaci stavu původního, tedy !full
. Funkci také
nastavíme do atributu onClick
elementu
<button>
. Soubor uložíme a můžeme si v prohlížeči
vyzkoušet, že schránka po kliknutí opravdu mění svůj stav a zobrazuje
jiný emoji:
Vzhled emoji se bude lišit dle vašeho operačního systému.
Proč nepoužijeme proměnné?
Možná vás napadlo, proč používat nějaké stavy, když můžeme stejně
dobře vytvořit jen obyčejnou true
/false
proměnnou,
která by stav poštovní schránky určovala? Stavy mají navíc
vnitřní logiku, kterou pak využívá vykreslovací
jádro React. Používáním stavů se při jejich změně daná
součást komponenty sama překreslí, aniž bychom se o to museli starat.
Všimněte si, že jsme při kliknutí na tlačítko jeho obsah jinak neměnili,
změnili jsme jen stav. To by s obyčejnou proměnnou nefungovalo.
Alternativní zápisy
Nakonec si opět ukažme několik alternativních zápisů, abyste uměli číst další aplikace.
Použití arrow funkce
K obsluze události můžeme použít arrow funkci, čímž se vyhneme deklaraci handler funkce:
import React, { useState } from 'react'; function MailboxButton() { const [full, setFull] = useState(false); return ( <button onClick={() => setFull(!full)}> {full ? '📬' : '📪'} </button> ); } export default MailboxButton;
Používání arrow funkcí při renderování v React způsobí, že se při každém překreslení vytvoří nová funkce. U složitějších projektů může být proto z hlediska výkonu lepší použít klasickou handler funkci.
Co ale nefunguje je zavolání setteru přímo v parametru bez arrow funkce:
import React, { useState } from 'react'; function MailboxButton() { const [full, setFull] = useState(false); return ( <button onClick={setFull(!full)}> {full ? '📬' : '📪'} </button> ); } export default MailboxButton;
Tím bychom totiž přes JSX setter rovnou zavolali (bez kliknutí na tlačítko). A React má ochranu před tím, aby změny stavů probíhaly bezprostředně za sebou, čímž se může aplikace zacyklit. Aplikace nám pak spadne s chybou: "Too many re-renders. React limits the number of renders to prevent an infinite loop":
Stejnou chybu bychom dostali, kdybychom se snažili např. nastavit stav přímo v metodě komponenty, tedy aby se hned po nastavení zas změnil:
import React, { useState } from 'react'; function MailboxButton() { const [full, setFull] = useState(false); setFull(true); // Tento řádek způsobí chybu return ( <button onClick={() => setFull(!full)}> {full ? '📬' : '📪'} </button> ); } export default MailboxButton;
Dvě nejdůležitější součásti React aplikací, vlastnosti a stavy, máme tímto probrané
V následujícím cvičení, Řešené úlohy k 1.-5. lekci React, si procvičíme nabyté zkušenosti z předchozích lekcí.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 90x (177.05 kB)
Aplikace je včetně zdrojových kódů v jazyce JavaScript