useCallback — concepte cheie, hook-ul de memoizare a funcțiilor în React

Autor: IT Sectr Publicat: 2026-07-04 Timp de citire: 9 min

useCallback — este un hook React care returnează o versiune memoizată a funcției, care nu se schimbă între render-uri atâta timp cât nu se schimbă dependențele ei. Spre deosebire de declararea obișnuită a unei funcții în interiorul componentei (care creează o funcție nouă la fiecare render), useCallback stabilizează referința funcției, prevenind astfel re-renderurile inutile ale componentelor copil optimizate prin React.memo. Conform React Documentation (2025), useCallback este util doar împreună cu React.memo sau cu hook-uri care depind de o referință stabilă.

Elemente cheie

  • useCallback — hook pentru memoizarea funcției, care păstrează referința între render-uri.
  • React.memo — useCallback este eficient doar împreună cu React.memo pentru prevenirea re-renderurilor.
  • Dependențe — funcția este recreată doar la schimbarea valorilor specificate din tablou.
  • Referință stabilă — useCallback garantează că referința funcției nu se va schimba fără necesitate.
  • Nu abuza — useCallback excesiv fără React.memo înrăutățește performanța.

Ce este useCallback în React

useCallback — este un hook adăugat în React 16.8 care memoizează funcția: returnează aceeași referință atâta timp cât dependențele nu se schimbă. Fără useCallback, fiecare declarare a unei funcții în interiorul componentei creează un obiect funcție nou la fiecare render. Pentru primitive acest lucru este imperceptibil, dar la transmiterea unor astfel de callback-uri către componentele copil optimizate prin React.memo, fiecare referință nouă provoacă re-renderul componentei copil.

Sintactic, useCallback este echivalent cu useMemo pentru funcție: useCallback(fn, deps) — este o prescurtare pentru useMemo(() => fn, deps). React stochează funcția memoizată în stocarea internă a nodului fiber și compară dependențele la fiecare render. Dacă dependențele nu s-au schimbat (Object.is pentru fiecare element), este returnată funcția anterioară.

Conform React Documentation — useCallback (2025), nu ar trebui să înfășori fiecare funcție în useCallback. Hook-ul are un cost propriu: apelul hook-ului, compararea dependențelor și alocarea memoriei pentru tabloul de dependențe. Dacă componenta este simplă și nu are arbori adânci cu React.memo — useCallback va încetini doar aplicația. Optimizarea trebuie să fie măsurabilă, nu intuitivă.

jsx
import { useCallback } from 'react';

function Parent() {
    const [count, setCount] = useState(0);

    // Referință stabilă — aceeași funcție până la schimbarea deps
    const handleClick = useCallback(() => {
        setCount(prev => prev + 1);
    }, []);

    return <Child onClick={handleClick} />;
}

Cum funcționează memoizarea funcțiilor

Memoizarea în useCallback se bazează pe memorarea în cache a rezultatului apelului funcției. React păstrează closure-ul creat la momentul primului render și îl returnează la render-urile ulterioare atâta timp cât dependențele rămân neschimbate. În interiorul nodului fiber, fiecare apel useCallback creează un nod în lista legată a hook-urilor, unde sunt stocate dependențele anterioare și valoarea memoizată.

Procesul de comparare a dependențelor se realizează strict prin Object.is — este o comparație superficială, fără verificarea profundă a obiectelor sau tablourilor. Dacă în dependențe se află un obiect sau un tablou, referința nouă de la fiecare render va fi considerată o schimbare. De aceea, în tabloul de dependențe trebuie transmise valori primitive sau referințe stabile (de exemplu, din useRef sau useMemo).

Conform React Core Team — Optimization Guide (2024), costul memoizării include trei componente: alocarea tabloului de dependențe la fiecare render, parcurgerea și compararea elementelor prin Object.is și eventualul overhead al colectării gunoiului la recreare. Pentru o componentă cu sute de împachetări useCallback acest lucru poate deveni vizibil — de aceea selectivitatea în utilizarea hook-ului este critică.

ScenariuFără useCallbackCu useCallback
Crearea funcțieiNouă la fiecare renderAceeași la deps stabile
Transmiterea în React.memoCopilul se re-rendereazăCopilul nu se re-renderează
În tabloul useEffectEfectul se reporneșteEfectul este stabil
OverheadMinimCompararea dependențelor + memorie

useCallback și performanța

Există un mit răspândit că useCallback îmbunătățește automat performanța. În realitate, în izolare (fără React.memo) useCallback chiar încetinește ușor aplicația din cauza costului comparării dependențelor. Hook-ul aduce beneficii reale doar în trei scenarii: prevenirea re-renderurilor componentelor React.memo, stabilitatea callback-urilor în useEffect și transmiterea către hook-uri custom care depind de reference equality.

Regula este simplă: până nu descoperi o problemă de performanță prin React DevTools Profiler — nu folosi useCallback. Echipa React a subliniat de repetate ori că optimizarea prematură este rădăcina răului. Mai întâi scrie cod curat fără memoizare, măsoară, găsește blocajul în profiler și abia apoi adaugă useCallback acolo unde chiar este necesar.

jsx
// Optimizare măsurabilă: Child este înfășurat în React.memo
const Child = React.memo(({ onClick }) => {
    console.log('Child s-a re-renderat');
    return <button onClick={onClick}>Click</button>;
});

function Parent() {
    const handleClick = useCallback(() => {
        console.log('apăsat');
    }, []);
    return <Child onClick={handleClick} />;
}

Conform Dan Abramov — Before You memo() (2024), peste 90% dintre cazurile de utilizare a useCallback în proiectele open-source sunt redundante. Dezvoltatorii împachetează fiecare funcție „pentru orice eventualitate", fără să măsoare efectul. Alternativa: dacă componenta copil este grea și re-renderul ei este costisitor — React.memo + useCallback sunt justificate. Dacă componenta copil este ușoară — re-renderul este mai ieftin decât compararea dependențelor.

Când să folosești useCallback

Primul scenariu — React.memo. Dacă componenta copil este înfășurată în React.memo și primește o funcție callback ca prop, fără useCallback componenta copil va fi redesenată la fiecare render al părintelui, chiar dacă propriile date nu s-au schimbat. useCallback stabilizează referința, iar React.memo poate sări corect peste re-render.

Al doilea scenariu — useEffect cu callback în dependențe. Dacă funcția este transmisă în tabloul de dependențe al useEffect, fiecare referință nouă va reporni efectul. useCallback garantează că referința este stabilă, iar efectul se execută doar la schimbarea datelor reale, nu la fiecare render. Acest lucru este deosebit de important pentru abonamente și cereri.

  • React.memo children — previne re-renderul componentelor copil memoizate la transmiterea callback-urilor.
  • useEffect dependencies — stabilizează funcția în tabloul de dependențe al efectului, prevenind repornirile inutile.
  • Hook-uri custom — dacă hook-ul primește un callback și depinde de reference equality a acestuia, useCallback este obligatoriu.
  • Context value — dacă în value al contextului este transmisă o funcție, useMemo cu useCallback stabilizează referința.
jsx
// useCallback pentru dependență stabilă în useEffect
const fetchData = useCallback(async (id) => {
    const res = await fetch(`/api/${id}`);
    setData(res.data);
}, []); // referință stabilă, nu se recreează niciodată

useEffect(() => {
    fetchData(props.id);
}, [props.id, fetchData]); // efectul rulează doar la schimbarea props.id

useCallback vs useMemo

Principala diferență între useCallback și useMemo constă în ceea ce memoizează fiecare. useCallback memoizează funcția: useCallback(fn, deps) returnează fn (aceeași versiune sau cea anterioară). useMemo memoizează rezultatul apelului funcției: useMemo(() => computeExpensive(a, b), [a, b]) returnează valoarea calculată, nu funcția.

Tehnic, useCallback este zahăr sintactic peste useMemo: useCallback(fn, deps) este echivalent cu useMemo(() => fn, deps). Această sintaxă există doar pentru lizibilitate — pentru ca dezvoltatorul să vadă clar că se memoizează exact funcția, nu valoarea. Nu există diferență de performanță între useCallback și useMemo cu funcție — ele generează același cod.

HookMemoizeazăSintaxăUtilizare
useCallbackFuncția (referința)useCallback(fn, deps)Callback-uri pentru componentele copil
useMemoRezultatul calcululuiuseMemo(() => value, deps)Calcule costisitoare, memoizarea obiectelor
jsx
// Acestea sunt echivalente:
const handleClick = useCallback(() => doSomething(a, b), [a, b]);
const handleClick = useMemo(() => () => doSomething(a, b), [a, b]);

Greșeli tipice cu useCallback

Cea mai răspândită greșeală — înfășurarea inutilă a tuturor funcțiilor în useCallback fără React.memo pe componentele copil. Dacă componenta copil nu este înfășurată în React.memo, ea se va re-rendera oricum la fiecare render al părintelui, indiferent dacă referința callback-ului se schimbă sau nu. useCallback fără React.memo — costuri fără beneficii.

  • useCallback fără React.memo — componenta copil se re-renderează oricum; optimizarea este inutilă.
  • Dependențe uitate — dacă în interiorul callback-ului este folosită o variabilă neindicată în deps, callback-ul conține stale closure.
  • Memoizare excesivă — înfășurarea fiecărei funcții, inclusiv onClick-uri triviale cu console.log.
  • Obiecte și tablouri în dependențe — referința nouă la fiecare render este considerată o schimbare, memoizarea este inutilă.
jsx
// ❌ Inutil: fără React.memo pe child
const handle = useCallback(() => doStuff(), []);
<Child onClick={handle} />; // Child se re-renderează oricum fără React.memo

// ❌ Stale closure: dependență lipsă
const handle = useCallback(() => {
    console.log(count); // count este mereu 0 — stale closure!
}, []);

// ✅ Corect: include dependențele
const handle = useCallback(() => {
    console.log(count);
}, [count]);

Problema stale closure în useCallback se rezolvă prin indicarea tuturor variabilelor folosite în tabloul de dependențe. eslint-plugin-react-hooks cu exhaustive-deps verifică automat că toate variabilele din corpul callback-ului sunt prezente în tablou. Dacă callback-ul folosește setState, care nu se schimbă între render-uri, acesta poate fi indicat în siguranță în deps — React garantează stabilitatea setState.

Întrebări frecvente

Trebuie să înfășori fiecare funcție în useCallback?

Nu. useCallback are sens doar în trei cazuri: componenta copil este înfășurată în React.memo, funcția este folosită în tabloul de dependențe al useEffect sau funcția este transmisă unui hook custom care depinde de reference equality. În restul cazurilor useCallback adaugă overhead fără beneficii. Echipa React recomandă să scrii mai întâi fără optimizări și să le adaugi pe baza rezultatelor profilării.

Ce este mai rapid — useCallback sau o funcție nouă la fiecare render?

Pentru componentele simple — funcția nouă la fiecare render este puțin mai rapidă, deoarece useCallback consumă resurse pentru compararea dependențelor și alocarea tabloului. Pentru componentele cu arbori adânci React.memo — useCallback câștigă, prevenind re-renderul a mii de elemente copil. Măsoară și compară, nu ghici — folosește React DevTools Profiler pentru o evaluare obiectivă.

Pot folosi useCallback cu funcții async?

Da, useCallback funcționează cu funcții async exact ca și cu cele sincrone. Hook-ul memoizează funcția în sine, iar rezultatul (Promise) este returnat de fiecare dată la apel. Funcția asincronă în interiorul useCallback este un pattern răspândit pentru callback-uri stabile de încărcare a datelor folosite în useEffect: const fetchData = useCallback(async (id) => {...}, []).

Cum să depanezi problema cu useCallback și React.memo?

Folosește React DevTools Profiler — va arăta ce componente se re-renderează și de ce. Pentru verificarea programatică adaugă console.log sau useWhyDidYouUpdate — o bibliotecă care înregistrează cauza re-renderului. Principalele cauze: s-a schimbat un prop (inclusiv referința callback-ului), s-au schimbat state sau contextul. Dacă useCallback nu ajută — verifică că toate dependențele sunt indicate corect.

Cum să transmiți useCallback către componenta copil fără React.memo?

Chiar și fără React.memo, useCallback poate fi util în combinație cu useMemo pentru value-ul contextului. Dacă transmiți un obiect cu funcții către Context.Provider, înfășoară crearea obiectului în useMemo și fiecare funcție în useCallback. Acest lucru va preveni re-renderul tuturor consumatorilor contextului la schimbarea uneia dintre funcții. Dar pentru transmiterea directă a callback-urilor în props fără React.memo, useCallback nu aduce beneficii.

Concluzii

  • useCallback — hook pentru memoizarea funcției, care returnează o referință stabilă până la schimbarea dependențelor.
  • React.memo synergy — useCallback este eficient doar împreună cu React.memo pe componentele copil.
  • Compararea dependențelor — prin Object.is; obiectele/tablourile în deps strică memoizarea.
  • Măsoară, nu ghici — adaugă useCallback doar după descoperirea unui blocaj prin profiler.
  • Stable closure — include toate variabilele folosite în tabloul de dependențe, altfel — stale closure.
  • useCallback vs useMemo — useCallback memoizează funcția, useMemo — rezultatul calculului.
  • Redundanță — peste 90% dintre cazurile de utilizare a useCallback în proiectele reale sunt optimizare prematură.

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și