React Hooks, které potřebujete znát

Publikováno: 12.11.2019

React s verzí 16.8 přináší zásadní novinku, a tou jsou Hooks, které mají přinést řešení pro hned 3 největší problémy, se kterými se v Reactu potýkáme. Implementace komponent pomocí tříd, sdílení logiky, nepraktický způsob práce s životním cyklem komponent. Představíme vám React Hooks a na konci ukážeme funkční příklad.

Celý článek

Začneme hned prvním problémem, který se týká tříd v JavaScriptu a jejich použití pro definici komponent. V Reactu je možné vytvořit komponentu pomocí třídy anebo pomocí funkcí. Dlouho platilo to, že pokud chceme, aby komponenta měla vnitřní stav, či životní cyklus, musíme zvolit třídu. V opačném případě, ale můžeme použít funkci, která je syntakticky jednodušší a nemusíme zápasit s klíčovým slovem this, které činí vývojářům v JavaScriptu často problémy.

Hooks však představují primitiva, díky kterým může i funkcionální komponenta používat stav, životní cyklus a nejen to. Můžeme teď prohlásit, že díky Hooks pro nás může být funkcionální komponenta jasnou volbou, jelikož nemusíme řešit špatně odhalitelné bugy, které plynou ze zapomenutí bindování funkcí, případně použití arrow function, kvůli nelogickému chování klíčového slova this, které při použití s funkcemi odkazuje na kontext v místě volání funkce, ne její definice.

Sdílení logiky se, po zapovězení mixinů a příchodu tříd s ES2015, začalo řešit dvěma návrhovými vzory:

  • Higher Order Component a
  • Function as Child (nebo také Render Props).

Oba tyhle vzory mají své problémy. Function as Child sice menší než HOC, ale zůstává Wrapper Hell (vaši komponentu máte zabalenou do velkého počtu obalujících funkcí kvůli sdílení logiky, což značně komplikuje strukturu komponent). React nyní umožňuje psát vlastní Hooks, které problém se sdílením logiky komponent řeší konečně správnou cestou.

Hooks umožňují i práci s životním cyklem. Jdou na to trochu jinak než metody obhospodařující životní cyklus v třídě. Běžně se v třídách stávalo, že logika, která spolu souvisí, je rozprostřená do několika metod životního cyklu, což přehlednosti zrovna nepomáhá. React nám nabízí například Hook useEffect, který dokáže pokrýt funkci hned třech metod životního cyklu, které spolu přímo souvisí, a tak je kód centralizovaný na jednom místě.

Hooks, které potřebujete pro každodenní práci

Pro práci se stavem máme useState

Díky useState můžeme, jak název napovídá, používat stav i v rámci funkcionálních komponent. Hook useState očekává parametr, který se stane iniciální hodnotou stavu, která je nastavená pouze poprvé, když je komponenta vytvořena. Pokud používáme TypeScript, tak můžeme tento Hook použít jako generickou funkci a předat jí typ našeho stavu. Hook nám vrátí dvojici, kterou získáme pomocí destrukturalizace. První je náš stav, v tomto případě číselná hodnota. Druhá je pak funkce, kterou můžeme zavolat a předat ji nějakou hodnotu, která se stane novým stavem, případně jí můžeme předat callback, který má jako parametr předchozí stav, na jehož základě můžeme vypočítat stav nový (podobně to bylo u metody setState, kterou známe z komponent vyrobených pomocí tříd).

import React from 'react'

const [count, setCount] = React.useState(0)

Hook useState můžeme použít v komponentě kolikrát budeme chtít. Já osobně však používám jeden useState, který nenese primitivní hodnotu, ale objekt, do kterého můžu vložit, co potřebuji.

const [state, setState] = React.useState({ count: 0, ... })

Životní cyklus a useEffect

Tento Hook pokrývá hned tři momenty životního cyklu komponenty. Je to její zavedení do DOMu, její přerenderování, a případně její odebrání z DOMu. Praxe ukazuje, že často potřebujeme stejnou logiku provádět při zavedení komponenty do DOMu a při jejím přerenderování. Tento Hook se proto volá při obou těchto situacích.

Přes return může také vrátit callback, který funguje jako componentWillUnmount, a volá se při odebírání komponenty z DOMu.

useEffect(() => {
   // volá se při zavedení komponenty do DOMu a jejím přerenderování
   return () => {
      // volá se při odebírání komponenty z DOMu
   };
});

Určitě nastane situace, ve které nechceme, aby useEffect volal předaný callback pokaždé, ale pouze při změně nějakého parametru. Toho dosáhneme předáním pole parametrů, které pak useEffect kontroluje mezi přerenderováním komponenty a volá callback jen tehdy, pokud se hodnota změní.

useEffect(() => {
   // volá se při zavedení do DOMu a přerenderování, ale pouze pokud se změní props.value
}, [props.myValue]);

Nebo můžeme chtít použít useState čistě jen při zavedení komponenty do DOMu. Pak stačí toto pole předat prázdné. Pro přehlednost si můžete useEffect zabalit do vlastního Hooku, který příhodně pojmenujete useMount.

function useMount(effect) {
   useEffect(effect, [])
}

Dobré je si uvědomit, že useEffect probíhá až po vykreslení přerenderované komponenty v prohlížeči. Tedy ve chvíli, když uživatel vidí změny. Až poté se useEffect volá, aby zbytečně neblokoval fázi vykreslení. Většinou nám tento fakt nevadí, ale v případech, kdy chceme provést nějakou akci, která povede k dalšímu přerenderování komponenty a nechceme, aby uživatel viděl „probliknutí“ přerenderované komponenty, měli bychom použít obdobný Hook useLayout. Ten funguje úplně stejně, jen se volá ve stejnou chvíli, jako metody componentDidMount a componentDidUpdate v komponentách vytvořených pomocí třídy. Tyto metody se volají po zavedení změn po přerenderování komponenty do reálného DOMu, ale ještě před vykreslením těchto změn v prohlížeči.

Přístup k DOM elementům pomocí useRef

Občas potřebujeme nějaký ten rychlý a jednoduchý způsob přístupu k DOM elementu, a to nám právě umožňuje Hook useRef, který vrací objekt se členem „current“. Přes ten se dostaneme například k hodnotě inputu ve formuláři.

function TextInputWithButton() {
  const inputEl = React.useRef(null);

  const onButtonClick = () => {
    console.log(inputEl.current.value)
  };

  return (
    <div>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Get Input Value</button>
    </div>
  );
}

Navíc lze useRef použít k persistování nějaké hodnoty mezi přerenderováním komponenty. Můžeme například získat předchozí hodnotu props. Napíšeme si na to opět vlastní Hook.

function CounterDisplay(props) {
  const prevProps = usePrevious(props);
  return <h1>Now {props.count} {prevProps && prevProps.count}</h1>;
}

function usePrevious(value) {
  const ref = React.useRef();

  React.useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

…a řada dalších Hooks

React nabízí řadu vestavěných Hooks, které můžou být velmi užitečné. Například useContext pro snadné použití kontextu, useReduce pro sofistikovanou správu stavu na způsob Reduxu a další.

Jak si můžeme všimnou, Hooks jsou v zásadě jednoduché funkce, které nám navíc umožňují abstrahovat logiku mimo komponentu, a tak ji snadno sdílet a testovat.

Funkční ukázka

Připravil jsem pro vás jednoduchý todo list v CodeSandbox, na kterém předvádím použití základních Hooks rovnou s TypeScriptem, který je v posledních letech na vzestupu. Osobně ho doporučuji používat na všech typech projektů. Pokud uvažujete o přechodu na TypeScript, ale nechce se vám opouštět Babel (například kvůli rozmanitým možnostem konfigurace), doporučuji shlédnout krátký video tutoriál na youtube, kde ukazuji použití presetu pro Babel, který umožňuje snadnou integraci TypeScriptu.

Nahoru
Tento web používá k poskytování služeb a analýze návštěvnosti soubory cookie. Používáním tohoto webu s tímto souhlasíte. Další informace