NOVINKA - Online rekvalifikační kurz Python programátor. Oblíbená a studenty ověřená rekvalifikace - nyní i online.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

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 hookem useCallback(),
  • 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:

Hotová aplikace - Pokročilý React

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ě:

Tlačítko již máme - Pokročilý React
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 hookem useMemo(),
  • 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

 

Předchozí článek
Hooky v Reactu - useState(), useEffect() a useRef()
Všechny články v sekci
Pokročilý React
Přeskočit článek
(nedoporučujeme)
Hooky v Reactu - useContext() a useReducer()
Článek pro vás napsala Laura Baluchová
Avatar
Uživatelské hodnocení:
10 hlasů
Aktivity