useCallback — concetti chiave, l'hook di memoizzazione delle funzioni in React

Autore: IT Sectr Pubblicato: 2026-07-04 Tempo di lettura: 9 min

useCallback è un hook React che restituisce una versione memoizzata di una funzione che non cambia tra i render finché le sue dipendenze non cambiano. A differenza di una dichiarazione di funzione normale all'interno di un componente (che crea una nuova funzione a ogni render), useCallback stabilizza il riferimento alla funzione, prevenendo re-render non necessari dei componenti figli ottimizzati con React.memo. Secondo React Documentation (2025), useCallback è utile solo in combinazione con React.memo o hook che dipendono da un riferimento stabile.

Punti chiave

  • useCallback — un hook per memoizzare una funzione, preservando il riferimento tra i render.
  • React.memo — useCallback è efficace solo con React.memo per prevenire re-render.
  • Dipendenze — la funzione viene ricreata solo quando i valori specificati nell'array cambiano.
  • Riferimento stabile — useCallback garantisce che il riferimento alla funzione non cambi inutilmente.
  • Non abusare — l'uso eccessivo di useCallback senza React.memo danneggia le prestazioni.

Cos'è useCallback in React

useCallback è un hook aggiunto in React 16.8 che memoizza una funzione: restituisce lo stesso riferimento finché le dipendenze non cambiano. Senza useCallback, ogni dichiarazione di funzione all'interno di un componente crea un nuovo oggetto funzione a ogni render. Per i primitivi questo è impercettibile, ma quando si passano questi callback a componenti figli ottimizzati con React.memo, ogni nuovo riferimento causa un re-render del componente figlio.

Sintatticamente, useCallback è equivalente a useMemo per una funzione: useCallback(fn, deps) è una scorciatoia per useMemo(() => fn, deps). React memorizza la funzione memoizzata nell'archivio interno del nodo fiber e confronta le dipendenze a ogni render. Se le dipendenze non sono cambiate (Object.is per ogni elemento), viene restituita la funzione precedente.

Secondo React Documentation — useCallback (2025), non dovresti avvolgere ogni funzione in useCallback. L'hook ha il suo costo: chiamare l'hook, confrontare le dipendenze e allocare memoria per l'array di dipendenze. Se un componente è semplice e non ha alberi profondi con React.memo, useCallback rallenterà solo l'applicazione. L'ottimizzazione dovrebbe essere misurabile, non intuitiva.

jsx
import { useCallback } from 'react';

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

    // Riferimento stabile — stessa funzione fino al cambio delle deps
    const handleClick = useCallback(() => {
        setCount(prev => prev + 1);
    }, []);

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

Come funziona la memoizzazione delle funzioni

La memoizzazione in useCallback si basa sulla memorizzazione nella cache del risultato della chiamata di funzione. React preserva la chiusura (closure) creata al momento del primo render e la restituisce nei render successivi finché le dipendenze rimangono invariate. All'interno del nodo fiber, ogni chiamata useCallback crea un nodo nella lista collegata degli hook, dove vengono memorizzate le dipendenze precedenti e il valore memoizzato.

Il confronto delle dipendenze viene eseguito rigorosamente tramite Object.is — un confronto superficiale senza controllo approfondito di oggetti o array. Se una dipendenza è un oggetto o un array, un nuovo riferimento a ogni render sarà considerato una modifica. Pertanto, l'array di dipendenze dovrebbe contenere valori primitivi o riferimenti stabili (ad esempio, da useRef o useMemo).

Secondo React Core Team — Optimization Guide (2024), il costo della memoizzazione include tre componenti: allocare l'array di dipendenze a ogni render, iterare e confrontare gli elementi tramite Object.is e il potenziale overhead di garbage collection durante la ricreazione. Per un componente con centinaia di wrapper useCallback, questo può diventare evidente — quindi la selettività nell'uso dell'hook è fondamentale.

ScenarioSenza useCallbackCon useCallback
Creazione funzioneNuova a ogni renderUguale con deps stabili
Passato a React.memoFiglio si re-renderizzaFiglio non si re-renderizza
Nell'array di useEffectEffetto si riavviaEffetto stabile
OverheadMinimoConfronto dipendenze + memoria

useCallback e prestazioni

Esiste un mito diffuso che useCallback migliori automaticamente le prestazioni. In realtà, isolatamente (senza React.memo), useCallback rallenta anche leggermente l'applicazione a causa del costo del confronto delle dipendenze. L'hook porta un reale beneficio solo in tre scenari: prevenire re-render dei componenti React.memo, stabilizzare i callback in useEffect e passare callback a hook personalizzati che dipendono dall'uguaglianza di riferimento.

La regola è semplice: finché non rilevi un problema di prestazioni con React DevTools Profiler — non usare useCallback. Il team di React ha ripetutamente sottolineato che l'ottimizzazione prematura è la radice di tutti i mali. Prima scrivi codice pulito senza memoizzazione, misura, trova il collo di bottiglia nel profiler e solo allora aggiungi useCallback dove è davvero necessario.

jsx
// Ottimizzazione misurabile: Child è avvolto in React.memo
const Child = React.memo(({ onClick }) => {
    console.log('Child ri-renderizzato');
    return <button onClick={onClick}>Click</button>;
});

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

Secondo Dan Abramov — Before You memo() (2024), oltre il 90% dei casi d'uso di useCallback nei progetti open-source sono ridondanti. Gli sviluppatori avvolgono ogni funzione "per sicurezza" senza misurarne l'effetto. L'alternativa: se il componente figlio è pesante e il suo re-render è costoso — React.memo + useCallback è giustificato. Se il componente figlio è leggero — il re-render è più economico del confronto delle dipendenze.

Quando usare useCallback

Il primo scenario è React.memo. Se un componente figlio è avvolto in React.memo e riceve una funzione callback come prop, senza useCallback il componente figlio si re-renderizza a ogni render del genitore, anche se i suoi dati non sono cambiati. useCallback stabilizza il riferimento, permettendo a React.memo di saltare correttamente il re-render.

Il secondo scenario è useEffect con un callback nelle dipendenze. Se una funzione viene passata all'array di dipendenze di useEffect, ogni nuovo riferimento riavvierà l'effetto. useCallback garantisce che il riferimento sia stabile e che l'effetto venga eseguito solo quando i dati effettivi cambiano, non a ogni render. Questo è particolarmente importante per sottoscrizioni e richieste.

  • Figli React.memo — previene il re-render dei componenti figlio memoizzati quando si passano callback.
  • Dipendenze di useEffect — stabilizza la funzione nell'array di dipendenze dell'effetto, evitando riavvii inutili.
  • Hook personalizzati — se un hook accetta un callback e dipende dalla sua uguaglianza di riferimento, useCallback è obbligatorio.
  • Valore del contesto — se una funzione viene passata nel valore del contesto, useCallback stabilizza il riferimento.
jsx
// useCallback per dipendenza useEffect stabile
const fetchData = useCallback(async (id) => {
    const res = await fetch(`/api/${id}`);
    setData(res.data);
}, []); // riferimento stabile, mai ricreato

useEffect(() => {
    fetchData(props.id);
}, [props.id, fetchData]); // effetto eseguito solo quando props.id cambia

useCallback vs useMemo

La differenza principale tra useCallback e useMemo è ciò che ciascuno memoizza. useCallback memoizza una funzione: useCallback(fn, deps) restituisce fn (la stessa o la versione precedente). useMemo memoizza il risultato di una chiamata di funzione: useMemo(() => computeExpensive(a, b), [a, b]) restituisce il valore calcolato, non una funzione.

Tecnicamente, useCallback è zucchero sintattico su useMemo: useCallback(fn, deps) è equivalente a useMemo(() => fn, deps). Questa sintassi esiste solo per la leggibilità — affinché lo sviluppatore veda chiaramente che viene memoizzata una funzione, non un valore. Non c'è differenza di prestazioni tra useCallback e useMemo con una funzione — generano codice identico.

HookMemoizzaSintassiUtilizzo
useCallbackFunzione (riferimento)useCallback(fn, deps)Callback per componenti figli
useMemoRisultato di calcolouseMemo(() => value, deps)Calcoli costosi, memoizzazione oggetti
jsx
// Questi sono equivalenti:
const handleClick = useCallback(() => doSomething(a, b), [a, b]);
const handleClick = useMemo(() => () => doSomething(a, b), [a, b]);

Errori comuni con useCallback

L'errore più comune è avvolgere inutilmente tutte le funzioni in useCallback senza React.memo sui componenti figli. Se un componente figlio non è avvolto in React.memo, si re-renderizza comunque a ogni render del genitore, indipendentemente dal fatto che il riferimento del callback cambi o meno. useCallback senza React.memo è costo senza beneficio.

  • useCallback senza React.memo — il componente figlio si re-renderizza comunque; l'ottimizzazione è inutile.
  • Dipendenze dimenticate — se una variabile usata all'interno del callback non è in deps, il callback contiene una stale closure.
  • Memoizzazione eccessiva — avvolgere ogni funzione, inclusi onClick banali con console.log.
  • Oggetti e array nelle dipendenze — un nuovo riferimento a ogni render è considerato una modifica, la memoizzazione è inutile.
jsx
// ❌ Inutile: nessun React.memo sul figlio
const handle = useCallback(() => doStuff(), []);
<Child onClick={handle} />; // Child si re-renderizza comunque senza React.memo

// ❌ Stale closure: dipendenza mancante
const handle = useCallback(() => {
    console.log(count); // count è sempre 0 — stale closure!
}, []);

// ✅ Corretto: includere le dipendenze
const handle = useCallback(() => {
    console.log(count);
}, [count]);

Il problema della stale closure in useCallback si risolve includendo tutte le variabili utilizzate nell'array di dipendenze. eslint-plugin-react-hooks con exhaustive-deps verifica automaticamente che tutte le variabili dal corpo del callback siano presenti nell'array. Se il callback usa setState, che non cambia tra i render, può essere incluso tranquillamente in deps — React garantisce la stabilità di setState.

Domande frequenti

Bisogna avvolgere ogni funzione in useCallback?

No. useCallback ha senso solo in tre casi: il componente figlio è avvolto in React.memo, la funzione è usata nell'array di dipendenze di useEffect, o la funzione è passata a un hook personalizzato che dipende dall'uguaglianza di riferimento. Negli altri casi, useCallback aggiunge overhead senza beneficio. Il team React consiglia di scrivere prima senza ottimizzazioni e aggiungerle in base ai risultati del profiling.

Cosa è più veloce — useCallback o una nuova funzione a ogni render?

Per componenti semplici — una nuova funzione a ogni render è leggermente più veloce, poiché useCallback spende risorse per confrontare le dipendenze e allocare l'array. Per componenti con alberi React.memo profondi, useCallback vince prevenendo il re-render di migliaia di elementi figli. Misura e confronta invece di indovinare — usa React DevTools Profiler per una valutazione oggettiva.

Si può usare useCallback con funzioni async?

Sì, useCallback funziona con funzioni async esattamente come con quelle sincrone. L'hook memoizza la funzione stessa e il risultato (una Promise) viene restituito ogni volta che viene chiamata. Una funzione async dentro useCallback è un pattern comune per callback stabili di caricamento dati usati in useEffect: const fetchData = useCallback(async (id) => {...}, []).

Come debuggare problemi con useCallback e React.memo?

Usa React DevTools Profiler — mostra quali componenti si re-renderizzano e perché. Per verifica programmatica, aggiungi console.log o usa useWhyDidYouUpdate — una libreria che registra la causa del re-render. Le cause principali: una prop è cambiata (incluso il riferimento del callback), lo stato è cambiato o il contesto è cambiato. Se useCallback non aiuta — verifica che tutte le dipendenze siano correttamente specificate.

Come passare useCallback a un componente figlio senza React.memo?

Anche senza React.memo, useCallback può essere utile in combinazione con useMemo per i valori del contesto. Se stai passando un oggetto con funzioni a Context.Provider, avvolgi la creazione dell'oggetto in useMemo e ogni funzione in useCallback. Questo impedisce il re-render di tutti i consumatori del contesto quando una delle funzioni cambia. Ma per passare callback direttamente nelle props senza React.memo, non c'è beneficio da useCallback.

Riepilogo

  • useCallback — un hook per memoizzare una funzione, restituendo un riferimento stabile fino al cambio delle dipendenze.
  • Sinergia con React.memo — useCallback è efficace solo con React.memo sui componenti figli.
  • Confronto dipendenze — tramite Object.is; oggetti/array in deps rompono la memoizzazione.
  • Misura, non indovinare — aggiungi useCallback solo dopo aver rilevato un collo di bottiglia nel profiler.
  • Chiusura stabile — includi tutte le variabili usate nell'array di dipendenze, altrimenti — stale closure.
  • useCallback vs useMemo — useCallback memoizza una funzione, useMemo memoizza il risultato di un calcolo.
  • Eccesso — oltre il 90% degli usi di useCallback nei progetti reali è ottimizzazione prematura.

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche