useCallback — egy React hook, amely a függvény memoizált változatát adja vissza, ami nem változik a renderelések között, amíg a függőségei nem változnak. Ellentétben a függvény komponensen belüli szokásos deklarálásával (amely minden renderelésnél új függvényt hoz létre), a useCallback stabilizálja a függvényre mutató referenciát, ami megakadályozza a React.memo-val optimalizált gyermekkomponensek felesleges újrarenderelését. A React Documentation (2025) szerint a useCallback csak a React.memo-val párosítva vagy stabil referenciától függő hook-okkal együtt hasznos.
A lényeg
useCallback — a React 16.8-ban hozzáadott hook, amely memoizálja a függvényt: addig ugyanazt a referenciát adja vissza, amíg a függőségek nem változnak. useCallback nélkül a komponensen belüli függvény minden deklarálása minden renderelésnél új függvényobjektumot hoz létre. Primitíveknél ez észrevehetetlen, de az ilyen callback-ek React.memo-val optimalizált gyermekkomponenseknek való átadásakor minden új referencia a gyermekkomponens újrarenderelését okozza.
Szintaktikailag a useCallback egyenértékű a useMemo-val függvényeknél: a useCallback(fn, deps) — rövidítése a useMemo(() => fn, deps) kifejezésnek. A React a memoizált függvényt a fiber-csomópont belső tárhelyén tárolja, és minden renderelésnél összehasonlítja a függőségeket. Ha a függőségek nem változtak (Object.is minden elemre), az előző függvény kerül visszaadásra.
A React Documentation — useCallback (2025) szerint nem szabad minden függvényt useCallback-be csomagolni. A hook-nak saját ára van: a hook meghívása, a függőségek összehasonlítása és memóriaallokáció a függőségi tömbhöz. Ha a komponens egyszerű, és nincsenek mély fái React.memo-val — a useCallback csak lassítani fogja az alkalmazást. Az optimalizációnak mérhetőnek kell lennie, nem intuitívnak.
import { useCallback } from 'react';
function Parent() {
const [count, setCount] = useState(0);
// Stabil referencia — ugyanaz a függvény, amíg a deps nem változik
const handleClick = useCallback(() => {
setCount(prev => prev + 1);
}, []);
return <Child onClick={handleClick} />;
}
A useCallback memoizációja a függvényhívás eredményének gyorsítótárazásán alapul. A React megtartja az első rendereléskor létrehozott closure-t, és a következő rendereléseknél visszaadja, amíg a függőségek változatlanok maradnak. A fiber-csomóponton belül minden useCallback-hívás csomópontot hoz létre a hook-ok láncolt listájában, ahol az előző függőségeket és a memoizált értéket tárolják.
A függőségek összehasonlításának folyamata szigorúan az Object.is segítségével történik — ez felszínes összehasonlítás, objektumok vagy tömbök mély vizsgálata nélkül. Ha a függőségekben objektum vagy tömb van, a minden renderelésnél keletkező új referencia változásként lesz értelmezve. Ezért a függőségi tömbben primitív értékeket vagy stabil referenciákat (például useRef-ből vagy useMemo-ból) kell átadni.
A React Core Team — Optimization Guide (2024) szerint a memoizáció költsége három összetevőből áll: a függőségi tömb allokációja minden renderelésnél, az elemek bejárása és összehasonlítása Object.is segítségével, valamint az újraalkotás során a szemétgyűjtés lehetséges többletterhelése. Egy több száz useCallback-csomagolást tartalmazó komponensnél ez észrevehetővé válhat — ezért a szelektivitás kritikus a hook használatában.
| Forgatókönyv | useCallback nélkül | useCallback-kel |
|---|---|---|
| Függvény létrehozása | Új minden renderelésnél | Ugyanaz stabil deps esetén |
| Átadás React.memo-nak | A gyermek újrarenderelődik | A gyermek nem renderelődik újra |
| useEffect tömbben | Az effekt újraindul | Az effekt stabil |
| Többletterhelés | Minimális | Függőségek összehasonlítása + memória |
Elterjedt mítosz, hogy a useCallback automatikusan javítja a teljesítményt. Valójában elszigetelten (React.memo nélkül) a useCallback kissé le is lassítja az alkalmazást a függőségek összehasonlításának költsége miatt. A hook csak három forgatókönyvben hoz valódi hasznot: a React.memo-komponensek újrarenderelésének megelőzése, a callback-ek stabilitása a useEffect-ben, és átadás a reference equality-től függő egyedi hook-oknak.
A szabály egyszerű: amíg nem találsz teljesítményproblémát a React DevTools Profiler segítségével — ne használj useCallback-et. A React csapata többször hangsúlyozta, hogy a korai optimalizálás a rossz gyökere. Először írj tiszta kódot memoizáció nélkül, mérj, találd meg a szűk keresztmetszetet a profilozóban, és csak utána add hozzá a useCallback-et ott, ahol valóban szükséges.
// Mérhető optimalizálás: a Child React.memo-ba van csomagolva
const Child = React.memo(({ onClick }) => {
console.log('A Child újrarenderelődött');
return <button onClick={onClick}>Click</button>;
});
function Parent() {
const handleClick = useCallback(() => {
console.log('kattintva');
}, []);
return <Child onClick={handleClick} />;
}
A Dan Abramov — Before You memo() (2024) szerint a useCallback használatának több mint 90%-a nyílt forráskódú projektekben felesleges. A fejlesztők „minden eshetőségre" beburkolnak minden függvényt anélkül, hogy megmérnék a hatást. Alternatíva: ha a gyermekkomponens nehéz, és az újrarenderelése drága — a React.memo + useCallback indokolt. Ha a gyermekkomponens könnyű — az újrarenderelés olcsóbb, mint a függőségek összehasonlítása.
Az első forgatókönyv — React.memo. Ha a gyermekkomponens React.memo-ba van csomagolva, és callback függvényt fogad prop-ként, akkor useCallback nélkül a gyermekkomponens a szülő minden renderelésénél újrarajzolódik, még akkor is, ha a saját adatai nem változtak. A useCallback stabilizálja a referenciát, így a React.memo helyesen tudja kihagyni az újrarenderelést.
A második forgatókönyv — useEffect callback-kel a függőségek között. Ha a függvényt a useEffect függőségi tömbjébe adják át, minden új referencia újraindítja az effektet. A useCallback garantálja, hogy a referencia stabil, és az effekt csak a valódi adatok változásakor fut le, nem minden renderelésnél. Ez különösen fontos az előfizetések és kérések esetén.
// useCallback stabil useEffect-függőséghez
const fetchData = useCallback(async (id) => {
const res = await fetch(`/api/${id}`);
setData(res.data);
}, []); // stabil referencia, soha nem jön újra létre
useEffect(() => {
fetchData(props.id);
}, [props.id, fetchData]); // az effekt csak a props.id változásakor fut
A legfőbb különbség a useCallback és a useMemo között az, hogy melyik mit memoizál. A useCallback a függvényt memoizálja: a useCallback(fn, deps) az fn-t adja vissza (ugyanazt vagy az előző verziót). A useMemo a függvényhívás eredményét memoizálja: a useMemo(() => computeExpensive(a, b), [a, b]) a kiszámított értéket adja vissza, nem a függvényt.
Technikailag a useCallback szintaktikai cukor a useMemo felett: a useCallback(fn, deps) egyenértékű a useMemo(() => fn, deps) kifejezéssel. Ez a szintaxis csak az olvashatóság kedvéért létezik — hogy a fejlesztő világosan lássa, hogy pontosan a függvény kerül memoizálásra, nem pedig az érték. Nincs teljesítménybeli különbség a useCallback és a függvényes useMemo között — ugyanazt a kódot generálják.
| Hook | Memoizál | Szintaxis | Használat |
|---|---|---|---|
| useCallback | A függvényt (referenciát) | useCallback(fn, deps) | Callback-ek gyermekkomponenseknek |
| useMemo | A számítás eredményét | useMemo(() => value, deps) | Drága számítások, objektumok memoizálása |
// Ezek egyenértékűek:
const handleClick = useCallback(() => doSomething(a, b), [a, b]);
const handleClick = useMemo(() => () => doSomething(a, b), [a, b]);
A leggyakoribb hiba — az összes függvény értelmetlen beburkolása useCallback-be React.memo nélkül a gyermekkomponenseken. Ha a gyermekkomponens nincs React.memo-ba csomagolva, akkor minden renderelésnél újrarenderelődik, függetlenül attól, hogy a callback referenciája változik-e vagy sem. A React.memo nélküli useCallback — költség haszon nélkül.
// ❌ Haszontalan: nincs React.memo a child-on
const handle = useCallback(() => doStuff(), []);
<Child onClick={handle} />; // A Child React.memo nélkül is újrarenderelődik
// ❌ Stale closure: hiányzó függőség
const handle = useCallback(() => {
console.log(count); // a count mindig 0 — stale closure!
}, []);
// ✅ Helyes: tüntesd fel a függőségeket
const handle = useCallback(() => {
console.log(count);
}, [count]);
A useCallback stale closure problémája úgy oldható meg, hogy a függőségi tömbben feltünteted az összes használt változót. Az exhaustive-deps-t használó eslint-plugin-react-hooks automatikusan ellenőrzi, hogy a callback törzsében lévő összes változó szerepel-e a tömbben. Ha a callback olyan setState-et használ, amely nem változik a renderelések között, nyugodtan feltüntethető a deps-ben — a React garantálja a setState stabilitását.
Gyakran ismételt kérdések
Nem. A useCallback csak három esetben bír értelmet: a gyermekkomponens React.memo-ba van csomagolva, a függvényt a useEffect függőségi tömbjében használják, vagy a függvényt olyan egyedi hook-nak adják át, amely a reference equality-től függ. Más esetekben a useCallback csak többletterhelést ad haszon nélkül. A React csapata azt javasolja, hogy előbb optimalizálás nélkül írj, és a profilozás eredményei alapján add hozzá őket.
Egyszerű komponenseknél a minden renderelésnél új függvény valamivel gyorsabb, mert a useCallback erőforrásokat költ a függőségek összehasonlítására és a tömb allokációjára. A mély React.memo fákkal rendelkező komponenseknél a useCallback nyer, megakadályozva több ezer gyermekelem újrarenderelését. Mérj és hasonlíts össze, ne találgass — használd a React DevTools Profilert az objektív értékeléshez.
Igen, a useCallback pontosan ugyanúgy működik az async függvényekkel, mint a szinkronokkal. A hook magát a függvényt memoizálja, az eredményt (Promise) pedig minden hívásnál visszaadja. Az aszinkron függvény a useCallback belsejében gyakori minta a useEffect-ben használt stabil adatbetöltő callback-eknél: const fetchData = useCallback(async (id) => {...}, []).
Használd a React DevTools Profilert — megmutatja, mely komponensek renderelődnek újra és miért. Programozott ellenőrzéshez adj hozzá console.log-ot vagy useWhyDidYouUpdate-ot — egy olyan könyvtárat, amely naplózza az újrarenderelés okát. A fő okok: megváltozott egy prop (beleértve a callback referenciát), megváltozott a state vagy a kontextus. Ha a useCallback nem segít — ellenőrizd, hogy minden függőség helyesen van feltüntetve.
React.memo nélkül is hasznos lehet a useCallback a useMemo-val kombinálva a kontextus value-jához. Ha függvényeket tartalmazó objektumot adsz át a Context.Provider-nek, csomagold az objektum létrehozását useMemo-ba, és minden függvényt useCallback-be. Ez megakadályozza a kontextus összes fogyasztójának újrarenderelését az egyik függvény változásakor. De a callback-ek React.memo nélküli, props-ban való közvetlen átadásánál a useCallback nem jelent előnyt.
Összegzés
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is