Lekce 6 - Hooky v Reactu - useCallback() a useMemo()
V předchozí lekci, Hooky v Reactu - useState(), useEffect() a useRef(), jsme si ukázali práci s hooky useState(), useEffect() a useRef().
V tomto tutoriálu pokročilého Reactu budeme pokračovat v
tématu hooků. Podíváme se na dva hooky sloužící ke cachování
dat mezi renderováním v React aplikaci. Konkrétně to budou hooky
useCallback()
a useMemo()
.
Hook useCallback()
Tento hook slouží k optimalizaci výkonu komponent.
Umožňuje vytvořit a uchovat v paměti referenci na funkci
tak, aby se tato funkce znovu vytvářela pouze tehdy, když se změní
konkrétní závislosti. React při použití useCallback()
funkci
pouze vrací, nevolá ji. Hook je užitečný především v situacích, v
nichž chceme zabránit zbytečnému vytváření nových funkcí při každém
vykreslení komponenty.
Syntaxe hooku useCallback()
vypadá takto:
const memoizedFunction= useCallback( () => { // funkce, kterou chceme memoizovat }, [závislosti] // pole závislostí );
Kód si popíšeme:
memoizedFunction
je do proměnné uložená funkce, která bude memoizována a vrácena hookemuseCallback()
,- jako první argument hook přijímá funkci, která má být memoizována,
- druhým argumentem je pole závislostí, které obsahuje proměnné, na kterých daná funkce závisí. Funkce se vytvoří znovu pouze tehdy, jestliže se některá z těchto proměnných změní.
Kdy použít useCallback()
Typickou situací pro použití useCallback()
je moment, kdy
předáváme funkci jako prop (vlastnost) do komponenty, která je
optimalizována pomocí funkce memo()
. Pomocí memo()
zamezujeme překreslování komponenty v případě, že její props
zůstávají stejné.
V JavaScriptu však funkce vždy vytváří novou funkci. Proto funkce
posouvaná pomocí props do komponenty bude při každém renderování
považovaná za funkci novou, i když se nijak nezmění. Optimalizace pouze
pomocí memo()
v tomto případě nebude fungovat, dokud
posouvanou funkci neobalíme hookem useCallback()
.
Nejlepší bude ukázat si to na konkrétním příkladu
Funkce memo()
a hook useMemo()
jsou
dvě odlišné věci. Funkce memo()
slouží k memoizaci celé
komponenty. Hook useMemo()
slouží k memoizaci výsledku výpočtu
nebo hodnoty v rámci komponenty.
Použití useCallback()
v
aplikaci
Budeme vycházet z aplikace z lekce Hooky v Reactu - useState(), useEffect() a useRef().
V ní si také stáhneme projekt, který teď společně upravíme. Výsledná aplikace bude vypadat takto:

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
Element input
již nebudeme potřebovat, proto v komponentě
MainCompChild.jsx
smažeme proměnnou inputRef
a hook
useEffect()
.
Smažeme také input
z vykreslované části.
Pak přidáme novou funkcionalitu. Importujeme si rovnou všechny hooky, které využijeme:
import { useState, useCallback, useMemo } from "react";
Hned pod console.log
pak do funkce MainCompChild()
vložíme kód:
const [childCount, setChildCount] = useState(0); const decrementChildCount = () => { console.log("Provádím výpočet pro dítě"); setChildCount((prevCount) => prevCount - 1); };
Vytvoříme stav, kde si ukládáme číslo, od kterého pomocí funkce
decrementChildCount()
odčítáme jedničku. Funkce se později
spustí po kliknutí na tlačítko.
V části za return
pak pod nadpisem <h3>
vypíšeme aktuální stav proměnné childCount
:
<p>Počet: {childCount}</p>
Přidání komponenty
Button
Teď si do aplikace přidáme vlastní komponentu pro vykreslení tlačítka. Tato komponenta bude přijímat jako prop funkci, která se spustí po kliknutí na tlačítko.
Vytvoříme si složku UI/
v adresáři src/
a v
ní soubor Button.jsx
s následujícím kódem:
import { memo } from "react"; function Button({onClick, children}) { console.log(`tlacitko render ${children}`); return ( <div> <button onClick={onClick}> {children} </button> </div> ); } export default memo(Button);
Po každém renderování tlačítka si vypíšeme do konzole, že render
proběhl a o které tlačítko jde. Tlačítko si exportujeme pomocí funkce
memo()
. To nám zajistí, že se překreslí pouze tehdy,
změní-li se jeho prop.
Teď tlačítko použijeme v souboru MainCompChild.jsx
. Nejprve
si ho importujeme:
import Button from './UI/Button';
Pod vykreslení odstavce <p>
s počtem vložíme kód:
<Button onClick={decrementChildCount}>-</Button>
Aplikace teď vypadá následovně:

Implementace hooku
useCallback()
Naše tlačítko je exportované pomocí funkce memo()
.
Očekáváme proto, že se opětovně vykreslí pouze tehdy, pokud se změní
jeho props.
Když si však otevřeme vývojářské nástroje, promažeme konzoli a
klikneme na tlačítko Plus v hlavní komponentě nebo na tlačítko
Mínus v dětské komponentě, vidíme, že se pokaždé znovu
vykresluje tlačítko s minusem, které je udělané přes naši vlastní
<Button>
komponentu. V konzoli vidíme při každém
vykreslení tlačítka vypsaný nadpis tlacitko render -
.
Je to proto, že React opakovaně při každém renderu vytváří
funkci decrementChildCount()
, kterou tlačítko v dětské
komponentě přijímá jako prop. Proto se tlačítko i přes použití
memo()
opětovně vykresluje.
To opravíme použitím hooku useCallback()
, do kterého
obalíme funkci decrementChildCount()
v souboru
MainCompChild.jsx
. Funkci nahradíme kódem:
const decrementChildCount = useCallback(() => { console.log("Provádím výpočet pro dítě"); setChildCount((prevCount) => prevCount - 1); }, []);
Pole závislostí je prázdné a funkce proto zůstává pokaždé stejná. Když se teď podíváme do konzole ve vývojářských nástrojích a klikneme na Plus nebo Mínus, vidíme, že tlačítko s minusem se vykresluje pouze při prvotním renderování, pak již nikoliv.
Hook useMemo()
Hook useMemo()
slouží k memoizaci výpočtů a
hodnot, a tím pomáhá optimalizovat výkon komponent. Memoizace
znamená uchování výsledku výpočtu a jeho opětovné použití, pokud se
vstupní závislosti nezmění. Koncept již známe z hooku
useCallback()
. Jen tentokrát neukládáme celou funkci, ale pouze
její výsledek.
Syntaxe hooku vypadá takto:
const memoizedValue = useMemo(() => { // náš výpočet nebo transformace dat }, [závislosti]); // pole závislostí;
Podívejme se na kód blíže:
memoizedValue
je uložený výsledek výpočtu nebo transformace dat, který bude memoizován a vrácen hookemuseMemo()
,- jako první argument hook přijímá výpočet nebo transformaci dat, která má být memoizována,
- jako druhý argument pak vložíme pole závislostí, které obsahuje proměnné, na nichž závisí náš výpočet. Pokud se některá z těchto proměnných změní, výpočet se provede znovu. Jinak se vrátí uložená hodnota z předešlého renderování.
Kdy použít useMemo()
Do hooku useMemo()
se obvykle ukládají náročné
kalkulace, jejichž závislosti se mění pouze ojediněle. Jinak
platí to, co pro useCallback()
. Hook oceníme, pokud je hodnota
posouvaná v prop komponentě obalené funkcí memo()
nebo je
hodnota využitá jako závislost jiného hooku.
Pojďme si teď ukázat konkrétní příklad použití hooku
useMemo()
.
Použití useMemo()
v
aplikaci
Budeme pokračovat v práci s naší aplikací. Otevřeme si soubor
MainCompChild.jsx
. Pod funkci decrementChildCount()
vložíme novou funkci, kterou pak rovnou zavoláme pro childCount
a výsledek uložíme do proměnné doubleWithoutMemo
:
const countDouble = ((childCount) => { console.log("krat dva"); return childCount * 2; }); const doubleWithoutMemo = countDouble(childCount);
Pod tlačítko Mínus pak vypíšeme výsledek:
<p>Počet krát dva: {doubleWithoutMemo}</p>
Otevřeme si vývojářské nástroje. V konzoli po kliknutí na tlačítko
Plus vidíme, že proběhne výpočet zdvojnásobení proměnné
childCount
a v konzoli se vypíše text krát dva
. To
je však zbytečné, jelikož se tato proměnná nemění.
Pomocí hooku useMemo()
teď docílíme toho, že se výpočet
opakovaně provede pouze při změně stavu childCount
, tedy po
kliknutí na tlačítko Mínus. Proměnnou
doubleWithoutMemo
nahradím kódem:
const memoizedVal = useMemo(() => countDouble(childCount), [childCount, countDouble]);
A k Počet krát dva vykreslíme hodnotu proměnné
memoizedVal
:
<p>Počet krát dva: {memoizedVal}</p>
Teď jsme v situaci, kdy je naše funkce countDouble()
závislostí hooku useMemo()
. Pro správnou funkcionalitu ji proto
ještě zabalíme do hooku useCallback()
, ať se závislost hooku
useMemo()
nemění při každém renderování.
Funkci countDouble()
nahradíme kódem:
const countDouble = useCallback(((childCount) => { console.log("krat dva"); return childCount * 2; }), []);
Hotovo V konzoli
vývojářských nástrojů teď po kliknutí na Plus vidíme, že
výpočet zdvojnásobení proměnné
childCount
již zbytečně
neprobíhá. Dosáhli jsme toho díky využití kombinace hooků
useCallback()
a useMemo()
.
V následující lekci, Hooky v Reactu - useContext() a useReducer(), se seznámíme s hooky
useContext()
a useReducer()
a na praktickém
příkladu si ukážeme, jak je použít.
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 9x (47.42 kB)
Aplikace je včetně zdrojových kódů v jazyce JavaScript