useCallback — je React hook, který vrací memoizovanou verzi funkce, která se nemění mezi rendery, dokud se nezmění její závislosti. Na rozdíl od běžného deklarování funkce uvnitř komponenty (které při každém renderu vytváří novou funkci) useCallback stabilizuje referenci na funkci, čímž zabraňuje zbytečným re-renderům dětských komponent optimalizovaných přes React.memo. Podle React Documentation (2025) je useCallback užitečný pouze ve spojení s React.memo nebo hooky, které závisí na stabilní referenci.
Hlavní body
useCallback — je hook přidaný v Reactu 16.8, který memoizuje funkci: vrací stejnou referenci, dokud se závislosti nezmění. Bez useCallback každé deklarování funkce uvnitř komponenty vytváří při každém renderu nový objekt funkce. U primitiv je to nepostřehnutelné, ale při předávání takových callbacků dětským komponentám optimalizovaným přes React.memo každá nová reference způsobuje re-render dětské komponenty.
Syntakticky je useCallback ekvivalentní useMemo pro funkci: useCallback(fn, deps) — je zkratka pro useMemo(() => fn, deps). React ukládá memoizovanou funkci do vnitřního úložiště uzlu fiber a při každém renderu porovnává závislosti. Pokud se závislosti nezměnily (Object.is pro každý prvek), vrací se předchozí funkce.
Podle React Documentation — useCallback (2025) není třeba obalovat každou funkci do useCallback. Hook má svou cenu: volání hooku, porovnávání závislostí a alokace paměti pro pole závislostí. Pokud je komponenta jednoduchá a nemá hluboké stromy s React.memo — useCallback bude aplikaci jen zpomalovat. Optimalizace musí být měřitelná, ne intuitivní.
import { useCallback } from 'react';
function Parent() {
const [count, setCount] = useState(0);
// Stabilní reference — stejná funkce, dokud se deps nezmění
const handleClick = useCallback(() => {
setCount(prev => prev + 1);
}, []);
return <Child onClick={handleClick} />;
}
Memoizace v useCallback je založena na ukládání výsledku volání funkce do mezipaměti. React uchovává closure vytvořené v okamžiku prvního renderu a vrací je při dalších renderech, dokud závislosti zůstávají nezměněné. Uvnitř uzlu fiber každé volání useCallback vytváří uzel v propojeném seznamu hooků, kde se ukládají předchozí závislosti a memoizovaná hodnota.
Proces porovnávání závislostí probíhá přísně přes Object.is — jde o povrchní porovnání bez hluboké kontroly objektů nebo polí. Pokud je v závislostech objekt nebo pole, nová reference při každém renderu bude považována za změnu. Proto je třeba do pole závislostí předávat primitivní hodnoty nebo stabilní reference (například z useRef nebo useMemo).
Podle React Core Team — Optimization Guide (2024) zahrnuje náklad memoizace tři složky: alokaci pole závislostí při každém renderu, procházení a porovnávání prvků přes Object.is a potenciální overhead z garbage collection při znovuvytváření. U komponenty se stovkami useCallback obalů se to může stát znatelným — proto je selektivita v používání hooku kritická.
| Scénář | Bez useCallback | S useCallback |
|---|---|---|
| Vytvoření funkce | Nová při každém renderu | Stejná při stabilních deps |
| Předání do React.memo | Dítě se re-renderuje | Dítě se nere-renderuje |
| V poli useEffect | Efekt se restartuje | Efekt je stabilní |
| Overhead | Minimální | Porovnání závislostí + paměť |
Je rozšířený mýtus, že useCallback automaticky zlepšuje výkon. Ve skutečnosti v izolaci (bez React.memo) useCallback aplikaci dokonce mírně zpomaluje kvůli nákladům na porovnávání závislostí. Hook přináší skutečný užitek pouze ve třech scénářích: zabránění re-renderům React.memo komponent, stabilita callbacků v useEffect a předání custom hookům, které závisí na reference equality.
Pravidlo je jednoduché: dokud nenajdeš problém s výkonem přes React DevTools Profiler — useCallback nepoužívej. React tým opakovaně zdůrazňoval, že předčasná optimalizace je kořenem zla. Nejprve napiš čistý kód bez memoizace, změř, najdi úzké hrdlo v profileru a teprve poté přidej useCallback tam, kde je skutečně potřeba.
// Měřitelná optimalizace: Child je obalen v React.memo
const Child = React.memo(({ onClick }) => {
console.log('Child se re-renderoval');
return <button onClick={onClick}>Click</button>;
});
function Parent() {
const handleClick = useCallback(() => {
console.log('kliknuto');
}, []);
return <Child onClick={handleClick} />;
}
Podle Dan Abramov — Before You memo() (2024) je více než 90 % případů použití useCallback v open-source projektech nadbytečných. Vývojáři obalují každou funkci „pro jistotu", aniž by měřili účinek. Alternativa: pokud je dětská komponenta těžká a její re-render je nákladný — React.memo + useCallback jsou opodstatněné. Pokud je dětská komponenta lehká — re-render je levnější než porovnávání závislostí.
První scénář — React.memo. Pokud je dětská komponenta obalena v React.memo a přijímá callback funkci jako prop, bez useCallback se dětská komponenta bude překreslovat při každém renderu rodiče, i když se její vlastní data nezměnila. useCallback stabilizuje referenci a React.memo může správně přeskočit re-render.
Druhý scénář — useEffect s callbackem v závislostech. Pokud se funkce předává do pole závislostí useEffect, každá nová reference restartuje efekt. useCallback zaručuje, že reference je stabilní, a efekt se spouští pouze při změně skutečných dat, ne při každém renderu. To je důležité zejména pro odběry a požadavky.
// useCallback pro stabilní závislost useEffect
const fetchData = useCallback(async (id) => {
const res = await fetch(`/api/${id}`);
setData(res.data);
}, []); // stabilní reference, nikdy se znovu nevytváří
useEffect(() => {
fetchData(props.id);
}, [props.id, fetchData]); // efekt běží pouze při změně props.id
Hlavní rozdíl mezi useCallback a useMemo je v tom, co každý z nich memoizuje. useCallback memoizuje funkci: useCallback(fn, deps) vrací fn (stejnou nebo předchozí verzi). useMemo memoizuje výsledek volání funkce: useMemo(() => computeExpensive(a, b), [a, b]) vrací vypočtenou hodnotu, nikoli funkci.
Technicky je useCallback syntaktický cukr nad useMemo: useCallback(fn, deps) je ekvivalentní useMemo(() => fn, deps). Tato syntaxe existuje pouze pro čitelnost — aby vývojář jasně viděl, že se memoizuje právě funkce, ne hodnota. Mezi useCallback a useMemo s funkcí není žádný výkonnostní rozdíl — generují stejný kód.
| Hook | Memoizuje | Syntaxe | Použití |
|---|---|---|---|
| useCallback | Funkci (referenci) | useCallback(fn, deps) | Callbacky pro dětské komponenty |
| useMemo | Výsledek výpočtu | useMemo(() => value, deps) | Nákladné výpočty, memoizace objektů |
// Tyto jsou ekvivalentní:
const handleClick = useCallback(() => doSomething(a, b), [a, b]);
const handleClick = useMemo(() => () => doSomething(a, b), [a, b]);
Nejčastější chybou je nesmyslné obalování všech funkcí do useCallback bez React.memo na dětských komponentách. Pokud dětská komponenta není obalena v React.memo, bude se re-renderovat při každém renderu rodiče bez ohledu na to, zda se reference callbacku mění či nikoli. useCallback bez React.memo — náklady bez přínosu.
// ❌ K ničemu: bez React.memo na child
const handle = useCallback(() => doStuff(), []);
<Child onClick={handle} />; // Child se bez React.memo stejně re-renderuje
// ❌ Stale closure: chybějící závislost
const handle = useCallback(() => {
console.log(count); // count je vždy 0 — stale closure!
}, []);
// ✅ Správně: zahrň závislosti
const handle = useCallback(() => {
console.log(count);
}, [count]);
Problém stale closure v useCallback se řeší uvedením všech použitých proměnných v poli závislostí. eslint-plugin-react-hooks s exhaustive-deps automaticky kontroluje, že všechny proměnné z těla callbacku jsou v poli přítomny. Pokud callback používá setState, který se mezi rendery nemění, lze jej bezpečně uvést v deps — React zaručuje stabilitu setState.
Často kladené dotazy
Ne. useCallback má smysl pouze ve třech případech: dětská komponenta je obalena v React.memo, funkce se používá v poli závislostí useEffect nebo se funkce předává custom hooku závislému na reference equality. V ostatních případech useCallback přidává overhead bez přínosu. React tým doporučuje nejprve psát bez optimalizací a přidávat je podle výsledků profilování.
U jednoduchých komponent je nová funkce při každém renderu o něco rychlejší, protože useCallback vynakládá prostředky na porovnávání závislostí a alokaci pole. U komponent s hlubokými stromy React.memo useCallback vítězí, protože zabraňuje re-renderu tisíců dětských prvků. Měř a porovnávej, nehádej — pro objektivní posouzení použij React DevTools Profiler.
Ano, useCallback funguje s async funkcemi přesně stejně jako se synchronními. Hook memoizuje samotnou funkci a výsledek (Promise) se vrací při každém volání. Asynchronní funkce uvnitř useCallback je častý vzor pro stabilní callbacky načítání dat používané v useEffect: const fetchData = useCallback(async (id) => {...}, []).
Použij React DevTools Profiler — ukáže, které komponenty se re-renderují a proč. Pro programovou kontrolu přidej console.log nebo useWhyDidYouUpdate — knihovnu, která loguje příčinu re-renderu. Hlavní příčiny: změnil se prop (včetně reference callbacku), změnil se state nebo kontext. Pokud useCallback nepomáhá — zkontroluj, že jsou všechny závislosti správně uvedeny.
I bez React.memo může být useCallback užitečný v kombinaci s useMemo pro value kontextu. Pokud předáváš objekt s funkcemi do Context.Provider, obal vytvoření objektu do useMemo a každou funkci do useCallback. Zabrání to re-renderu všech spotřebitelů kontextu při změně jedné z funkcí. Ale při přímém předávání callbacků v props bez React.memo useCallback nepřináší užitek.
Závěr
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také