Jak používat React Hooks: Kompletní průvodce s příklady
Jak používat React Hooks: Kompletní průvodce s příklady
Když se na začátku roku 2019 v Reactu 16.8 objevily Hooks, zásadně to změnilo přístup vývojářů k tvorbě React aplikací. Funkční komponenty díky nim mohly spravovat stav, vedlejší efekty a kontext bez nutnosti psát třídní komponenty. Porozumění tomu, jak efektivně používat React Hooks, je dnes nezbytné pro každého moderního React vývojáře, protože Hooks se staly „jediným způsobem, jak psát většinu React kódu“. Tento průvodce vás seznámí se základními vestavěnými Hooks, vysvětlí pravidla jejich používání a poskytne praktické příklady, které můžete ihned použít.
Co se naučíte
Na konci tohoto průvodce budete s jistotou používat základní React Hooks – useState, useEffect, useContext, useRef, useReducer, useMemo a useCallback – ve svých projektech. Pochopíte, kdy zvolit ten či onen Hook, jak se vyhnout běžným chybám, jako jsou zbytečné překreslování, a jak extrahovat znovupoužitelnou logiku do vlastních Hooků. Nejdůležitější poznatek: Hooks jsou jen funkce; umět je kombinovat je klíčem k ovládnutí Reactu.
Pochopení pravidel používání Hooků
Než se ponoříme do jednotlivých Hooků, je nutné si osvojit dvě neotřesitelná pravidla, jejichž dodržování zajišťuje balíček eslint-plugin-react-hooks:
Volejte Hooks pouze na nejvyšší úrovni. Nikdy nevolejte Hooks uvnitř cyklů, podmínek nebo vnořených funkcí. React spoléhá na pořadí volání Hooků pro uchování stavu mezi překresleními – pokud se pořadí změní, React nebude schopen správně přiřadit stav k jeho Hooku.
Volejte Hooks pouze z React funkcí. Hooks můžete volat pouze z funkčních komponent nebo z jiných vlastních Hooků. Běžná JavaScriptová funkce nemůže volat
useStatenebouseEffect.Google AdInline article slot
Z druhého pravidla vyplývá konvence: jakákoli funkce, která volá Hooks, by měla začínat use (například useCustomHook). Linter používá tuto předponu pro správné uplatnění pravidel.
Nejdůležitější Hooks, které budete používat denně
useState: Správa stavu komponenty
Hook useState je nejzákladnější – umožňuje přidávat stav do funkčních komponent. Vrací pole dvou prvků: aktuální hodnotu stavu a funkci pro jeho aktualizaci.
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0); // Počáteční stav – 0
return (
<button onClick={() => setCount(count + 1)}>
Kliknuto {count} krát
</button>
);
}
Při aktualizaci stavu na základě předchozí hodnoty používejte funkční formu aktualizace, abyste předešli závodním podmínkám, zejména s automatickým dávkováním Reactu:
// Bezpečné: používá předchozí hodnotu stavu
setCount(prevCount => prevCount + 1);
// Také bezpečné pro objekty: rozšiřuje předchozí stav
setUser(prevUser => ({ ...prevUser, name: 'Jane' }));
Pokud je výpočet počátečního stavu náročný na zdroje, předejte funkci místo hodnoty – React ji provede pouze při prvním překreslení:
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});
useEffect: Zpracování vedlejších efektů
Hook useEffect umožňuje provádět vedlejší efekty ve funkčních komponentách, jako je načítání dat, nastavování odběrů nebo ruční manipulace s DOM. Ve výchozím nastavení se efekty spouštějí po každém dokončeném překreslení, ale můžete řídit, kdy se spustí, pomocí pole závislostí.
import { useEffect, useState } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
// Tento efekt se spustí po připojení komponenty a při změně userId
fetchUser(userId).then(data => setUser(data));
// Volitelná funkce pro vyčištění se spustí před dalším efektem a při odpojení
return () => {
// Zrušení čekajících požadavků nebo vyčištění odběrů
};
}, [userId]); // Pole závislostí: opětovné spuštění pouze při změně userId
return <div>{user?.name}</div>;
}
Klíčové vzory pro useEffect:
- Prázdné pole závislostí
[]: Efekt se spustí jednou po prvním překreslení (připojení). - Bez pole závislostí: Efekt se spouští po každém překreslení (zřídka to, co potřebujete).
- Funkce pro vyčištění: Zabraňuje únikům paměti tím, že čistí odběry, časovače nebo čekající síťové požadavky.
Důležité upozornění: Ne každá změna stavu vyžaduje efekt. Pokud můžete hodnotu vypočítat během překreslování, udělejte to přímo, ne přes useEffect:
// ❌ Zbytečný efekt
const [fullName, setFullName] = useState('');
useEffect(() => {
setFullName(`${firstName} ${lastName}`);
}, [firstName, lastName]);
// ✅ Výpočet přímo během překreslování
const fullName = `${firstName} ${lastName}`;
useContext: Konzumace kontextu bez prop drillingu
Hook useContext umožňuje číst hodnoty z React Contextu bez obalování komponenty do Consumer:
import { useContext } from 'react';
// Vytvoření kontextu
const ThemeContext = React.createContext('light');
function ThemedButton() {
// Čtení hodnoty kontextu přímo
const theme = useContext(ThemeContext);
return <button className={theme}>Klikni na mě</button>;
}
Když se změní hodnota provideru kontextu, každá komponenta používající useContext se automaticky překreslí.
useRef: Odkazy na hodnoty, které nezpůsobují překreslení
Hook useRef vytváří měnitelný ref objekt, jehož vlastnost .current je zachována mezi překresleními. Na rozdíl od stavu, aktualizace ref nezpůsobuje překreslení. Díky tomu jsou refy ideální pro:
- Ukládání odkazů na DOM elementy
- Sledování identifikátorů časovačů
- Ukládání hodnot, které by neměly vyvolávat aktualizace UI
import { useRef, useEffect } from 'react';
function TextInput() {
const inputRef = useRef(null);
useEffect(() => {
// Zaměření na vstupní pole po připojení komponenty
inputRef.current.focus();
}, []);
return <input ref={inputRef} type="text" />;
}
useReducer: Správa složité logiky stavu
Pro stav zahrnující několik dílčích hodnot nebo složité přechody je useReducer často vhodnější než useState. Přijímá funkci reducer a počáteční stav, vrací aktuální stav a funkci dispatch:
import { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return { count: action.payload };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Počet: {state.count}
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset', payload: 0 })}>Reset</button>
</>
);
}
Hooks pro optimalizaci výkonu
useMemo: Ukládání náročných výpočtů do mezipaměti
useMemo ukládá výsledek výpočtu do mezipaměti a přepočítává jej pouze při změně závislostí:
import { useMemo } from 'react';
function TodoList({ todos, filter }) {
// Přepočet pouze při změně todos nebo filter
const filteredTodos = useMemo(() => {
return todos.filter(todo => todo.status === filter);
}, [todos, filter]);
return (
<ul>
{filteredTodos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
useCallback: Memoizace odkazů na funkce
useCallback vrací memoizovanou verzi funkce zpětného volání, která se mění pouze při změně závislostí. To je užitečné zejména při předávání callbacků optimalizovaným podřízeným komponentám, které spoléhají na referenční rovnost pro zabránění zbytečným překreslením:
import { useCallback } from 'react';
function ParentComponent() {
const handleClick = useCallback(() => {
console.log('Tlačítko bylo stisknuto');
}, []); // Odkaz na funkci zůstává stabilní mezi překresleními
return <ChildButton onClick={handleClick} />;
}
Vlastní Hooks: Znovupoužití logiky mezi komponentami
Skutečná síla Hooků se projeví při vytváření vlastních Hooků pro extrakci a znovupoužití logiky se stavem. Vlastní Hook je jednoduše JavaScriptová funkce, jejíž název začíná use a která může volat jiné Hooks.
Příklad: useLocalStorage
Tento vlastní Hook synchronizuje stav s localStorage, čímž uchovává data při obnovení stránky:
import { useState, useEffect } from 'react';
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
if (typeof window === 'undefined') return initialValue;
const stored = localStorage.getItem(key);
return stored !== null ? JSON.parse(stored) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
// Použití
function ThemeToggle() {
const [theme, setTheme] = useLocalStorage('theme', 'light');
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Přepnout na {theme === 'light' ? 'tmavý' : 'světlý'} režim
</button>
);
}
Příklad: useMediaQuery
Tento Hook umožňuje podmíněně vykreslovat komponenty na základě CSS media dotazů, například preference uživatele pro omezení pohybu:
import { useState, useEffect } from 'react';
function useMediaQuery(query) {
const [matches, setMatches] = useState(() => {
if (typeof window === 'undefined') return false;
return window.matchMedia(query).matches;
});
useEffect(() => {
const mediaQueryList = window.matchMedia(query);
const onChange = (event) => setMatches(event.matches);
mediaQueryList.addEventListener('change', onChange);
return () => mediaQueryList.removeEventListener('change', onChange);
}, [query]);
return matches;
}
// Použití
function MotionAwareComponent() {
const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)');
return prefersReducedMotion ? <StaticHero /> : <AnimatedHero />;
}
React 19: Nové Hooks pro formuláře a asynchronní data
React 19 (vydán v prosinci 2024) představil několik nových Hooků určených ke zjednodušení práce s formuláři a asynchronními operacemi.
useActionState: Zjednodušení odesílání formulářů
useActionState spravuje stav odesílání formuláře, vrací poslední výsledek, akční funkci a příznak čekání:
import { useActionState } from 'react';
async function subscribeAction(prevState, formData) {
const email = formData.get('email');
try {
await subscribe(email);
return { ok: true };
} catch (error) {
return { ok: false, error: error.message };
}
}
function NewsletterForm() {
const [state, formAction, isPending] = useActionState(
subscribeAction,
{ ok: false }
);
return (
<form action={formAction}>
<input name="email" type="email" required />
<button type="submit" disabled={isPending}>
{isPending ? 'Odebírání...' : 'Odebírat'}
</button>
{state.ok && <p>Jste přihlášeni!</p>}
{state.error && <p style={{ color: 'red' }}>{state.error}</p>}
</form>
);
}
useFormStatus: Přístup ke stavu nadřazeného formuláře
useFormStatus umožňuje vnořeným komponentám zjistit, zda se jejich nadřazený formulář odesílá:
import { useFormStatus } from 'react';
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Ukládání...' : 'Uložit'}
</button>
);
}
Integrace s Redux: Hook useSelector
Při použití Redux s React je Hook useSelector moderním doporučeným způsobem přístupu ke stavu z Redux store. Nahrazuje starou komponentu vyššího řádu connect a lépe funguje s TypeScript.
import { useSelector, useDispatch } from 'react-redux';
function CounterComponent() {
// Extrakce konkrétních dat z Redux store
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<span>{count}</span>
<button onClick={() => dispatch(increment())}>Zvýšit</button>
</div>
);
}
Důležité: useSelector ve výchozím nastavení používá striktní referenční rovnost ===. Vracení nového objektu při každém volání způsobí překreslení. Používejte memoizované selektory nebo volejte useSelector vícekrát pro jednotlivá pole, abyste optimalizovali výkon:
// ❌ Toto způsobuje překreslení při každé akci
const { count, user } = useSelector((state) => ({
count: state.count,
user: state.user
}));
// ✅ Volejte useSelector pro každé pole zvlášť
const count = useSelector((state) => state.count);
const user = useSelector((state) => state.user);
Zdroje
- Oficiální dokumentace Reactu: Vestavěné React Hooks
- Oficiální dokumentace Reactu: API reference Hooků
- Dokumentace React Redux: API Hooků
- Patterns.dev: Vzor Hooks
- Oficiální dokumentace Reactu: Použití State Hooku
— Editorial Team
Zatím žádné komentáře.