Lekce 5 - Hooky v Reactu - useState(), useEffect() a useRef()
V předchozí lekci, Serverové renderování podruhé - Stylování v Tailwind CSS, jsme dokončili serverové renderování a naši aplikaci nastylovali pomocí Tailwind CSS.
V dnešním tutoriálu pokročilého Reactu se blíže
seznámíme s hooky a na praktickém příkladu si ukážeme,
jak s nimi pracovat. Zaměříme se na použití hooků useState()
,
useEffect()
a useRef()
.
Co jsou to hooky
Hooky jsou funkce, které poskytuje React jako součást jeho API, a umožňují funkčním komponentám manipulovat s funkcionalitami, které byly dříve dostupné pouze pro třídové komponenty. Hooky byly představeny v Reactu od verze 16.8 a znamenaly velkou změnu ve způsobu psaní komponent.
S hooky můžeme do funkčních komponent přidávat různé
funkcionality a chování. Například useState()
slouží
pro ukládání stavu, useEffect()
pro práci s efekty a
useRef()
pro vytváření referencí na DOM prvky nebo jiné
hodnoty v komponentě.
Pro práci s hooky existuje několik pravidel. Hooky musejí být volány
pouze na nejvyšší úrovni funkční komponenty nebo v
jiných vlastních hook funkcích. Hooky naopak nemohou být volány v
rámci podmínek, smyček nebo vnořených funkcí.
Nemohou být volány ani v rámci třídových
komponent. V těch je správným způsobem používání metod jako
componentDidMount()
nebo componentDidUpdate()
.
Hook useState()
Tato funkce umožňuje komponentám ukládat a aktualizovat svůj vnitřní stav. Stav je zachován mezi re-rendery komponenty, což umožňuje uchovávat data a interagovat s nimi v průběhu života komponenty.
Inicializace stavu využívá destrukturalizaci pole hodnot, které funkce
useState()
vrací. Vypadá takto:
const [stateVariable, setStateVariable] = useState(0);
stateVariable
je proměnná, do které je uložena aktuální hodnota stavu. Při prvním renderování je to hodnota zadaná jako výchozí stav v závorceuseState()
, v našem příkladu0
.setStateVariable()
je funkce, kterou použijeme k aktualizaci stavu. Název funkce může být libovolný, ale konvence je používat slovoset
následované názvem stavu.
Důležitým poznatkem je to, že volání funkce pro změnu stavu nezmění
současný stav v kódu, který je právě vykonávaný. Ovlivní pouze to, co
funkce useState()
vrátí jako proměnnou od
následujícího renderování:
const [name, setName] = useState("Jiří"); function handleClick() { setName("Adam"); console.log(name); // Pořád Jiří! }
Funkce jako výchozí stav
Jako inicializační stav pro useState()
lze použít také
funkci. To je užitečné v situaci, kdy je počáteční stav
komponenty závislý na výpočtech nebo externím chování. Funkce
pro inicializaci stavu se spustí pouze jednou, když se
komponenta poprvé vykreslí. Funkce musí být čistá (pure), neměla by
přijímat žádné argumenty a měla by vracet hodnotu libovolného typu:
const [stateVariable, setStateVariable] = useState(() => new Date().getSeconds());
Aktualizace stavu závislého na předchozím stavu
Pro aktualizaci stavu v Reactu, kdy nová hodnota závisí na předchozím stavu, je důležité zajistit, že aktualizace bude prováděna atomicky a spolehlivě. Toho dosáhneme pomocí funkce, která akceptuje předchozí stav jako argument:
setStateVariable((prevState) => prevState + 1);
Hook useEffect()
Hook useEffect()
umožňuje provádět efekty v reakci na
změny stavu nebo životní cyklus komponenty. Vedlejší efekt
se spouští po vykreslení komponenty nebo když dojde ke změně některé ze
závislostí.
Inicializace hooku vypadá takto:
useEffect(() => { console.log("efekt proběhl") // Kód pro efekt }, [pole závislostí]);
- Prvním argumentem je funkce, která obsahuje kód, který se má provést při spuštění efektu.
- Druhým argumentem je pole závislostí (dependencies), které určuje, na co efekt reaguje. Pokud je prázdné, efekt se spustí pouze při prvním vykreslení komponenty a poté se už při změnách stavu nebo jiných faktorů znovu spouštět nebude. Pokud pole obsahuje závislosti, efekt se spustí pouze tehdy, změní-li se některá z těchto závislostí.
Pokud je pole závislostí prázdné, efekt bude fungovat jako
metoda componentDidMount()
v třídových komponentách.
Hook useRef()
Tento hook umožňuje referencovat a manipulovat s DOM elementy nebo jinými hodnotami uvnitř funkčních komponent.
Referenci vytvoříme pomocí useRef()
a přiřazením do
proměnné:
const myRef = useRef(0);
Referenci propojíme s DOM elementem tak, že ji přiřadíme atributu
ref
na elementu:
<input ref={myRef} />
Hook vrací objekt s jedinou vlastností. Nazývá se current
.
Vlastnost je prvotně nastavená na hodnotu, kterou jsme nastavili při
vytvoření reference pomocí useRef()
. V našem případě je to
0
. Hodnotu pak lze libovolně měnit:
myRef.current = 1;
Pro přístup k hodnotě reference použijeme zápis
myRef.current
:
console.log(myRef.current);
Pro referenci je klíčové, že její změna nezpůsobuje opětovné renderování komponenty. Proto je vhodná k ukládání předchozích hodnot mezi renderováním. To znamená, že reference jsou ideální pro ukládání informací, které neovlivňují vizuál komponenty.
Reference by se také neměla číst nebo měnit v průběhu renderování:
return <h1>{myRef.current}</h1>; // 🚩 Tento kód není v pořádku!
Pokud v kódu potřebujeme referenci číst nebo měnit v
průběhu renderování, je vhodné použít spíše stav pomocí hooku
useState()
.
Čtení hodnoty reference nebo její změna jsou naopak v pořádku
při obsluhování událostí. Například ve funkci s názvem
clickHandler()
nebo uvnitř funkce v hooku
useEffect()
.
Praktická ukázka použití hooků
Teď si teorii vyzkoušíme v praxi. Hooky využijeme v aplikaci, kterou jsme
vytvořili v lekci tohoto kurzu s názvem Vykreslování
v Reactu a jeho optimalizace. Tam si také stáhneme z archivu kód
aplikace, kterou společně rozšíříme. Naše aplikace bude nově
obsahovat input
, na který se vytvoří focus
pokaždé, když bude Počet liché číslo:

Příprava projektu
Stažený projekt si otevřeme v preferovaném editoru kódu. V terminálu si spustíme příkaz pro instalaci modulů a projekt spustíme na lokálním serveru:
Instalace modulů a spuštění projektu:
npm install
npm run dev
Hook useState()
v aplikaci
Hook useState()
již v aplikaci využíváme. Pro zopakování
si otevřeme soubor MainComponent.jsx
a podíváme se na
již napsaný kód:
import {useState} from 'react'; import MainCompChild from './MainCompChild'; function MainComponent() { console.log("hlavni komponenta"); const [count, setCount] = useState(0); const incrementCount = () => { setCount((prevCount) => prevCount + 1); // Zvýší stav o 1 vzhledem k předchozímu stavu }; return ( <div style={{border: "2px solid"}}> <h2>Hlavní komponenta</h2> <p>Počet: {count}</p> <button onClick={incrementCount}>+</button> <MainCompChild /> </div> ) } export default MainComponent
Shrneme si to podstatné:
- do souboru importujeme hook,
- inicializujeme stav
count
s funkcí pro změnusetCount
a počátečním stavem0
, - při kliknutí na tlačítko se spustí funkce
incerementCount()
, která zvýší stav proměnnécount
o1
vzhledem k předchozímu stavu.
Přidání hooků
useEffect()
a useRef()
Teď společně do aplikace přidáme další dva hooky. Nejdříve si
otevřeme soubor MainCompChild.jsx
, s nímž budeme pracovat.
Do souboru přidáme import hooků:
import { useEffect, useRef } from "react";
Teď si pod console.log
vytvoříme referenci:
const inputRef = useRef();
Nepotřebujeme výchozí hodnotu, jelikož referenci využijeme pro
vytvoření reference na DOM prvek, konkrétně input
. Tento
input
si teď do funkce přidáme pod <h3>
ve
vykreslovací části funkce:
<input type="text" placeholder="Focus při lichém počtu" ref={inputRef}></input>
Inputu přiřadíme vytvořenou referenci a v placeholderu vysvětlíme, čeho chceme docílit.
Pro hezčí vzhled přidáme přes inline styly margin divu, který obaluje vše, co funkce vrací:
<div style={{margin: 1 + "em"}}>
Teď potřebujeme zajistit, aby input
získal focus
pokaždé, když bude v proměnné count
liché číslo. Na to je
ideální hook useEffect()
. Ten vykoná potřebné úkony
pokaždé, když se změní proměnná count
, kterou mu přidáme
do pole závislostí.
Abychom znali aktuální stav proměnné count
, posuneme si ji z
rodičovské komponenty pomocí props
. V souboru
MainComponent.jsx
nahradíme vykreslení
<MainCompChild />
následujícím kódem:
<MainCompChild count={count} />
Vrátíme se zpět do souboru MainCompChild.jsx
a v definici
funkce jí přidáme props
, přičemž si proměnnou
count
rovnou vytáhneme pomocí destrukturalizace:
function MainCompChild({count})
Za vytvoření reference pak přidáme následující kód:
useEffect(() => { if (count % 2 !== 0) { inputRef.current.focus(); } }, [count]);
Pokaždé když se změní hodnota proměnné count
, funkce v
prvním argumentu hooku ověří, jestli je současný počet lichý. Jestliže
ano, zavolá metodu focus()
na komponentu v DOM propojenou s naší
referencí, tedy na input
. Jde o jeden z mála případů, kdy je
vhodné v Reactu přímo manipulovat s DOM elementy.
A to je všechno Když teď
klikáme na tlačítko plus, při lichých hodnotách počtu získá input
focus
. V aplikaci teď využíváme všechny tři představené
hooky.
V následující lekci, Hooky v Reactu - useCallback() a useMemo(), si vysvětlíme, jak fungují hooky
useCallback()
a useMemo()
, a ukážeme si praktickou
ukázku jejich využití.
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 7x (46.4 kB)
Aplikace je včetně zdrojových kódů v jazyce JavaScript