Cheat sheet degli hook di React: ogni hook con un esempio dal vivo

Gli hook di React sono funzioni come useState, useEffect e useRef che danno ai componenti funzione stato, side effect e ref. Questo cheat sheet copre ogni hook integrato in React 19.3 con lo scopo in una riga, un esempio dal vivo e l'errore più comune, più 12 custom hook da copiare.

Tutti gli hook integrati in sintesi

Al momento della stesura (ottobre 2026) l'ultima release di React è la 19.3. La tabella elenca ogni hook presente nel riferimento ufficiale degli hook di React, più i tre hook di React 19 documentati in pagine a parte (use, useOptimistic e l'hook di React DOM useFormStatus). Gli hook contrassegnati React 18 funzionano in React 18 e 19. Gli hook contrassegnati React 19 non esistono in React 18. Ogni hook di React 18 qui sotto ha un esempio dal vivo su cui puoi cliccare; quelli di React 19 sono mostrati solo come codice, perché questa pagina gira su React 18.

Visualizzati tutti i 20
HookGruppoServe aDa
useStateStatoconservare un valore che fa ri-renderizzare il componente quando cambiaReact 18
useReducerStatogestire uno stato con molti aggiornamenti correlati tramite un'unica funzione reducerReact 18
useContextContestoleggere un valore dal provider più vicino sopra il componenteReact 18
useRefRefconservare un valore mutabile o un nodo DOM che non provoca renderReact 18
useImperativeHandleRefscegliere quali metodi un genitore può chiamare tramite un refReact 18
useEffectEffettisincronizzarsi con qualcosa fuori da React dopo il paintReact 18
useLayoutEffectEffettimisurare il layout prima che il browser disegniReact 18
useInsertionEffectEffettiinserire regole di stile prima che girino i layout effect (librerie CSS-in-JS)React 18
useEffectEventEffettileggere le props più recenti dentro un effect senza rieseguirloReact 19.2
useMemoPrestazionimemorizzare un calcolo costoso tra un render e l'altroReact 18
useCallbackPrestazionimemorizzare una funzione così i figli memoizzati non si ri-renderizzanoReact 18
useTransitionPrestazionisegnare un aggiornamento di stato come non urgente e ottenere un flag di attesaReact 18
useDeferredValuePrestazionilasciare che una parte lenta della UI resti indietro rispetto alla digitazione veloceReact 18
useIdAltrigenerare ID univoci e stabili per label e attributi ARIAReact 18
useSyncExternalStoreAltriiscriversi a uno store che vive fuori da ReactReact 18
useDebugValueAltrietichettare un custom hook in React DevToolsReact 18
useActionStateAltriseguire il risultato e lo stato di attesa di una form actionReact 19
useOptimisticAltrimostrare il risultato atteso di un'azione prima che finiscaReact 19
useAPIleggere una promise o un context, anche dentro le condizioniReact 19
useFormStatusReact DOMleggere lo stato di attesa del form genitoreReact 19

Gli hook seguono due regole: chiamali solo al livello più alto di un componente o di un custom hook (mai dentro cicli, condizioni o funzioni annidate), e chiamali solo da componenti funzione React o da altri hook. use è l'unica eccezione alla prima regola: può essere chiamato dentro blocchi if e cicli. Il pacchetto eslint-plugin-react-hooks controlla entrambe le regole per te.

Hook di stato

useState

Cosa fa: Conserva un valore per questo componente. Chiamare il setter programma un nuovo render con il nuovo valore.

import { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <>
      <button onClick={() => setCount((c) => c - 1)}>-1</button>
      <span>{count}</span>
      <button onClick={() => setCount((c) => c + 1)}>+1</button>
    </>
  );
}

Errore comune: Scrivere setCount(count + 1) tre volte in un clic e aspettarsi +3. Tutte e tre leggono lo stesso count di questo render, quindi ottieni +1. Passa una funzione di aggiornamento, setCount(c => c + 1), quando il valore successivo dipende dal precedente. Il pulsante "+3 (updater)" qui sopra fa esattamente questo. Un secondo classico: modificare un oggetto o un array sul posto (user.name = "x"; setUser(user)). React confronta per riferimento, vede lo stesso oggetto e salta il render. Crea invece un nuovo oggetto: setUser({ ...user, name: "x" }).

useReducer

Cosa fa: Sposta la logica di aggiornamento in un'unica funzione pura che riceve lo stato attuale e un'azione e restituisce lo stato successivo. Utile quando più valori cambiano insieme.

type Action = { type: "add"; text: string } | { type: "toggle"; id: number };

function reducer(state: Todo[], action: Action): Todo[] {
  switch (action.type) {
    case "add":
      return [...state, { id: Date.now(), text: action.text, done: false }];
    case "toggle":
      return state.map((t) => (t.id === action.id ? { ...t, done: !t.done } : t));
  }
}

const [todos, dispatch] = useReducer(reducer, []);
dispatch({ type: "add", text: "Ship it" });

Errore comune: Eseguire side effect dentro il reducer (fetch, scritture su localStorage, generazione di ID casuali da un timer su cui conti in seguito). In sviluppo, con lo Strict Mode, React chiama i reducer due volte per far emergere il codice impuro. Mantieni il reducer puro e metti i side effect negli event handler o negli effect.

Hook di contesto

useContext

Cosa fa: Legge il valore del provider corrispondente più vicino sopra questo componente, e ri-renderizza quando quel valore cambia.

const ThemeContext = createContext<"light" | "dark">("light");

function App() {
  const [theme, setTheme] = useState<"light" | "dark">("light");
  return (
    <ThemeContext.Provider value={theme}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Badge() {
  const theme = useContext(ThemeContext);
  return <span className={theme}>Current theme: {theme}</span>;
}

Errore comune: Passare un nuovo oggetto letterale come valore del provider a ogni render, come value={{ user, setUser }}. Ogni render crea un nuovo oggetto, quindi ogni consumer si ri-renderizza anche quando non è cambiato nulla. Avvolgi il valore in useMemo, oppure separa i valori che cambiano di rado da quelli che cambiano spesso in due context. Controlla anche che il provider si trovi sopra il componente: chiamare useContext nello stesso componente che renderizza il provider restituisce il valore predefinito.

Hook per i ref

useRef

Cosa fa: Ti dà un contenitore (ref.current) che sopravvive ai render. Modificarlo non provoca un render. Usalo per nodi DOM, ID di timer e altri valori che lo schermo non mostra.

const inputRef = useRef<HTMLInputElement>(null);
const clicks = useRef(0);

<input ref={inputRef} />
<button onClick={() => inputRef.current?.focus()}>Focus</button>
<button onClick={() => { clicks.current += 1; }}>Count silently</button>

Errore comune: Mostrare ref.current nel JSX e aspettarsi che si aggiorni. I ref non provocano render, quindi lo schermo si allinea solo quando qualcos'altro ri-renderizza (prova il pulsante "Aggiungi al ref", poi "Ri-renderizza" qui sopra). Se il valore deve comparire sullo schermo, va nello stato. Evita anche di leggere o scrivere i ref durante il render, salvo per un'inizializzazione lazy una tantum.

useImperativeHandle

Cosa fa: Personalizza ciò che un genitore riceve quando mette un ref sul tuo componente, così esponi pochi metodi con un nome invece dell'intero nodo DOM.

type Handle = { shake: () => void; clear: () => void };

// React 18: wrap in forwardRef. In React 19 you can accept ref as a normal prop.
const FancyInput = forwardRef<Handle, Props>(function FancyInput(props, ref) {
  const el = useRef<HTMLInputElement>(null);
  useImperativeHandle(ref, () => ({
    shake() { /* animate */ el.current?.focus(); },
    clear() { if (el.current) el.current.value = ""; },
  }), []);
  return <input ref={el} />;
});

// parent
const ref = useRef<Handle>(null);
<FancyInput ref={ref} />
<button onClick={() => ref.current?.shake()}>Shake</button>

Errore comune: Usarlo per gestire cose che le props possono esprimere. Se un genitore chiama ref.current.open() per mostrare una modale, una prop isOpen di solito è più semplice e facile da seguire. Riserva gli handle imperativi a focus, scroll, play/pausa e animazioni. Dimenticare l'array delle dipendenze (il terzo argomento) ricrea inoltre l'handle a ogni render.

Hook per gli effect

useEffect

Cosa fa: Esegue codice dopo che React ha aggiornato lo schermo, per tenere un componente sincronizzato con qualcosa fuori da React: un timer, una sottoscrizione, una richiesta di rete, un widget non React. Restituisci una funzione di cleanup per annullarlo.

useEffect(() => {
  if (!running) return;
  const id = setInterval(() => setSeconds((s) => s + 1), 1000);
  return () => clearInterval(id); // runs before the next effect and on unmount
}, [running]);

Errore comune: Usare un effect per calcolare qualcosa che potresti calcolare durante il render, come useEffect(() => setFullName(first + " " + last), [first, last]). Costa un render in più e può mostrare valori vecchi per un frame. Scrivi invece const fullName = first + " " + last. Il secondo bug più comune è un cleanup mancante: in sviluppo, con lo Strict Mode, React monta, smonta e rimonta ogni componente una volta, quindi un effect senza cleanup si iscriverà o avvierà un timer due volte. Quella doppia esecuzione è un indizio, non un bug di React.

useLayoutEffect

Cosa fa: Stessa forma di useEffect, ma gira dopo che React modifica il DOM e prima che il browser disegni. Usalo per misurare un elemento e adattare il layout così gli utenti non vedono mai la posizione sbagliata.

const boxRef = useRef<HTMLSpanElement>(null);
const [width, setWidth] = useState(0);

useLayoutEffect(() => {
  setWidth(boxRef.current!.getBoundingClientRect().width);
}, [text]);

Errore comune: Usarlo di default. Blocca il disegno, quindi un lavoro lento al suo interno fa sembrare la pagina bloccata. Usa useEffect a meno che tu non veda uno sfarfallio che solo una misurazione prima del paint risolve (tooltip, posizionamento di popover, dimensionamento automatico). Inoltre non fa nulla durante il rendering lato server, il che produce un avviso in alcune configurazioni.

useInsertionEffect

Cosa fa: Gira prima di qualsiasi layout effect così una libreria può inserire regole <style> prima che qualcosa legga il layout. Esiste per gli autori di librerie CSS-in-JS.

function useScopedRule(selector: string, rule: string) {
  useInsertionEffect(() => {
    const style = document.createElement("style");
    style.textContent = `${selector} { ${rule} }`;
    document.head.appendChild(style);
    return () => style.remove();
  }, [selector, rule]);
}

Errore comune: Usarlo nel codice applicativo, o provare ad aggiornare lo stato da lì. Al suo interno non puoi programmare aggiornamenti e i ref non sono ancora collegati. Se non stai scrivendo una libreria di stili, quasi certamente ti serve useEffect o useLayoutEffect.

useEffectEvent

Cosa fa: React 19.2 e successivi. Avvolge una funzione così un effect può chiamarla e vedere sempre le props e lo stato più recenti, senza elencare quei valori come dipendenze. Usalo per la parte "evento" di un effect, come registrare un log o mostrare una notifica con il tema attuale.

// React 19.2+ only. Not run on this page.
import { useEffect, useEffectEvent } from "react";

function ChatRoom({ roomId, theme }) {
  const onConnected = useEffectEvent(() => {
    showNotification("Connected", theme); // always the latest theme
  });

  useEffect(() => {
    const conn = createConnection(roomId);
    conn.on("connected", () => onConnected());
    conn.connect();
    return () => conn.disconnect();
  }, [roomId]); // theme is not a dependency, and onConnected must not be listed
}

Errore comune: Mettere l'effect event nell'array delle dipendenze, oppure chiamarlo durante il render o passarlo ad altri componenti. La documentazione di React dice di chiamare gli effect event solo dall'interno degli effect (o di altri effect event), e la sua regola di lint avvisa quando ne elenchi uno come dipendenza. Su React 18 il sostituto abituale è il pattern "latest ref" usato da useInterval più in basso in questa pagina.

Hook per le prestazioni

useMemo

Cosa fa: Memorizza il risultato di un calcolo e lo ricalcola solo quando cambiano le sue dipendenze.

const filtered = useMemo(
  () => items.filter((item) => item.includes(query)),
  [items, query],
);

Errore comune: Avvolgere tutto con esso. Memoizzare lavoro economico come a + b o un map breve aggiunge codice e costo di confronto senza alcun guadagno. Misura prima (React DevTools Profiler) e memoizza i calcoli lenti, i valori passati a figli memo e i valori usati come dipendenze degli effect. Una trappola collegata: una dipendenza che è un nuovo oggetto a ogni render ({ sort: "asc" }) fa ricalcolare il memo ogni volta.

useCallback

Cosa fa: Restituisce la stessa istanza di funzione tra un render e l'altro finché una dipendenza non cambia. Conta quando la funzione è passata a un figlio avvolto in memo, o usata come dipendenza di un effect.

const handleAdd = useCallback(() => setCount((c) => c + 1), []);

const Child = memo(function Child({ onClick }: { onClick: () => void }) {
  return <button onClick={onClick}>Add</button>;
});

<Child onClick={handleAdd} />

Errore comune: Usare useCallback quando il figlio non è memoizzato. Senza memo sul figlio, questo si ri-renderizza comunque con il genitore, quindi la funzione stabile non cambia nulla. La demo dal vivo mostra entrambi i figli: solo quello che riceve la funzione memorizzata salta i render. Le closure obsolete sono l'altra trappola: se la callback legge count ma l'elenco delle dipendenze è vuoto, continua a vedere il primo valore. Usa una funzione di aggiornamento o elenca la dipendenza.

useTransition

Cosa fa: Segna un aggiornamento di stato come non urgente. React mantiene reattiva la schermata attuale mentre prepara quella nuova, e ti dà un flag isPending per un indicatore di caricamento.

const [isPending, startTransition] = useTransition();

function selectTab(next: string) {
  startTransition(() => {
    setTab(next);
  });
}

Errore comune: Avvolgere in una transition lo stato stesso di un campo di testo. Gli input controllati devono aggiornarsi con urgenza, altrimenti la digitazione sembra rotta. Mantieni normale lo stato dell'input e sposta nella transition l'aggiornamento lento e derivato (oppure usa useDeferredValue). In React 18 la funzione passata a startTransition deve essere sincrona; React 19 accetta anche funzioni async.

useDeferredValue

Cosa fa: Ti dà una copia di un valore a cui è permesso restare indietro. Le parti veloci della UI usano il valore reale; una parte lenta renderizza con quello differito e si allinea quando il browser è inattivo.

const [query, setQuery] = useState("");
const deferredQuery = useDeferredValue(query);
const isStale = query !== deferredQuery;

<input value={query} onChange={(e) => setQuery(e.target.value)} />
<div style={{ opacity: isStale ? 0.5 : 1 }}>
  <SlowResults query={deferredQuery} />
</div>

Errore comune: Dimenticare di avvolgere il figlio lento in memo. Se il figlio si ri-renderizza comunque con il genitore a ogni tasto premuto, differire la sua prop non aiuta. Non è nemmeno un debounce: non attende un tempo fisso e non riduce le richieste di rete. Per avere meno richieste, usa il debounce (vedi useDebounce più sotto).

Altri hook

useId

Cosa fa: Genera un ID univoco all'interno dell'app e identico sul server e sul client, per htmlFor, aria-describedby e attributi simili.

function Field({ label }: { label: string }) {
  const id = useId();
  return (
    <>
      <label htmlFor={id}>{label}</label>
      <input id={id} />
    </>
  );
}

Errore comune: Usarlo per creare le key delle liste. Le key dovrebbero venire dai tuoi dati (un ID dal database), non da un hook. Evita anche Math.random() per gli ID degli elementi nelle app renderizzate lato server: server e browser producono numeri diversi e l'hydration fallisce, ed è proprio il problema che useId risolve.

useSyncExternalStore

Cosa fa: Iscrive un componente a una fonte di dati che non appartiene a React (un'API del browser, uno store globale) e legge uno snapshot che resta coerente durante il rendering concorrente.

function subscribe(callback: () => void) {
  window.addEventListener("online", callback);
  window.addEventListener("offline", callback);
  return () => {
    window.removeEventListener("online", callback);
    window.removeEventListener("offline", callback);
  };
}

const online = useSyncExternalStore(
  subscribe,
  () => navigator.onLine, // client snapshot
  () => true,             // server snapshot
);

Errore comune: Definire subscribe inline dentro il componente. Una nuova funzione a ogni render fa sì che React annulli e rinnovi l'iscrizione ogni volta. Definiscila fuori dal componente o avvolgila in useCallback. Anche la funzione snapshot deve restituire lo stesso valore quando non è cambiato nulla: restituire un nuovo oggetto o array a ogni chiamata causa un ciclo di render infinito.

useDebugValue

Cosa fa: Aggiunge un'etichetta a un custom hook in React DevTools. Non ha alcun effetto su ciò che vedono gli utenti.

function useOnlineStatus() {
  const online = useSyncExternalStore(subscribe, () => navigator.onLine);
  useDebugValue(online ? "Online" : "Offline");
  return online;
}

Errore comune: Aggiungerlo a ogni custom hook o farci dentro formattazioni costose. Usalo per gli hook condivisi il cui stato è difficile da leggere in DevTools, e passa un formatter come secondo argomento se l'etichetta è costosa da costruire: useDebugValue(date, d => d.toISOString()).

Hook di React 19 (solo codice, non eseguibili su React 18)

Solo React 19. I quattro hook di questa sezione (use, useActionState, useOptimistic, useFormStatus) non sono esportati da React 18. Questa pagina gira su React 18, quindi sono mostrati come codice da copiare in un progetto React 19, senza demo dal vivo. useEffectEvent, trattato sopra, richiede React 19.2.

useActionState

Cosa fa: Avvolge una form action e ti dà il suo ultimo risultato più un flag di attesa. L'azione riceve lo stato precedente e il FormData inviato.

// React 19+
import { useActionState } from "react";

async function subscribe(prev: { error?: string; ok?: boolean }, formData: FormData) {
  const email = String(formData.get("email") ?? "");
  if (!email.includes("@")) return { error: "Enter a valid email" };
  await saveEmail(email);
  return { ok: true };
}

function Newsletter() {
  const [state, formAction, isPending] = useActionState(subscribe, {});
  return (
    <form action={formAction}>
      <input name="email" type="email" />
      <button disabled={isPending}>{isPending ? "Saving..." : "Subscribe"}</button>
      {state.error && <p role="alert">{state.error}</p>}
      {state.ok && <p>Thanks, you are subscribed.</p>}
    </form>
  );
}

Errore comune: Dimenticare che il primo argomento dell'azione è lo stato precedente. Scrivere async function subscribe(formData) significa che formData è in realtà il vecchio stato e .get lancia un errore. Nota anche che per un breve periodo si chiamava useFormState (in react-dom) durante la fase canary di React 19; la documentazione attuale usa useActionState da react.

useOptimistic

Cosa fa: Mostra una versione temporanea e ottimistica dello stato mentre un'azione async è in corso, poi torna allo stato reale quando finisce.

// React 19+
import { useOptimistic } from "react";

function Thread({ messages, send }: Props) {
  const [optimistic, addOptimistic] = useOptimistic(
    messages,
    (current, text: string) => [...current, { text, sending: true }],
  );

  async function action(formData: FormData) {
    const text = String(formData.get("text"));
    addOptimistic(text);
    await send(text); // parent updates `messages` when this resolves
  }

  return (
    <form action={action}>
      {optimistic.map((m, i) => <p key={i}>{m.text}{m.sending && " (sending)"}</p>)}
      <input name="text" />
    </form>
  );
}

Errore comune: Chiamare il setter ottimistico fuori da un'azione o da una transition. React se lo aspetta dentro una form action o startTransition; altrove il valore ottimistico compare e svanisce subito (React avvisa in sviluppo). E gestisci sempre l'errore: se send lancia un errore, mostra un messaggio così il contenuto non sparisce in silenzio.

use

Cosa fa: Legge il valore di una promise (sospendendo finché non si risolve) o di un context. A differenza degli altri hook, può essere chiamato dentro condizioni e cicli.

// React 19+
import { use, Suspense } from "react";

function Comments({ commentsPromise }: { commentsPromise: Promise<Comment[]> }) {
  const comments = use(commentsPromise); // suspends until resolved
  return comments.map((c) => <p key={c.id}>{c.text}</p>);
}

function Page({ commentsPromise }) {
  return (
    <Suspense fallback={<p>Loading comments...</p>}>
      <Comments commentsPromise={commentsPromise} />
    </Suspense>
  );
}

// Conditional context read, which useContext cannot do:
function Heading({ show }: { show: boolean }) {
  if (!show) return null;
  const theme = use(ThemeContext);
  return <h1 className={theme}>Hello</h1>;
}

Errore comune: Creare la promise dentro il componente che chiama use, come use(fetch(url)). Ogni render crea una nuova promise, quindi il componente resta sospeso per sempre. Crea la promise una volta sola (in un server component, un loader, una cache o un genitore) e passala verso il basso. Inoltre use non può stare dentro try/catch; gestisci gli errori con un error boundary.

useFormStatus

Cosa fa: Si importa da react-dom. Legge lo stato del <form> genitore: pending, data, method e action.

// React 19+
import { useFormStatus } from "react-dom";

function SubmitButton() {
  const { pending } = useFormStatus();
  return <button type="submit" disabled={pending}>{pending ? "Sending..." : "Send"}</button>;
}

function ContactForm() {
  return (
    <form action={sendMessage}>
      <textarea name="message" />
      <SubmitButton />
    </form>
  );
}

Errore comune: Chiamarlo nello stesso componente che renderizza il <form>. Riporta solo lo stato di un form sopra il componente chiamante, quindi nel componente del form stesso pending è sempre false. Sposta il pulsante in un componente a parte, come sopra.

L'array delle dipendenze, spiegato

useEffect, useLayoutEffect, useMemo, useCallback e useImperativeHandle ricevono un elenco di valori come ultimo argomento. React confronta ogni elemento con il valore del render precedente usando Object.is. Se un elemento è cambiato, l'effect gira di nuovo (o il memo ricalcola). Questa sola regola spiega ogni caso:

ScriviL'effect giraUso tipico
useEffect(fn)dopo ogni renderraro; di solito è segno che qualcosa andrebbe calcolato durante il render
useEffect(fn, [])una volta dopo il mount (due volte in sviluppo con lo Strict Mode), cleanup all'unmountsottoscrizioni e setup che non usano props né stato
useEffect(fn, [a, b])dopo il mount, poi ogni volta che a o b cambiafetch per ID, sincronizzazione con una prop

Clicca i pulsanti: cambiare b riesegue solo l'effect senza array, cambiare a riesegue sia quello sia l'effect [a], e l'effect [] resta fermo dopo il mount.

Cinque regole che prevengono la maggior parte dei bug sulle dipendenze

  1. Elenca ogni valore reattivo che la callback legge. Props, stato e qualsiasi cosa dichiarata nel corpo del componente. Dimenticarne uno ti dà una closure obsoleta: la callback conserva il valore di un vecchio render. Installa eslint-plugin-react-hooks e lascia che exhaustive-deps ti dica cosa manca.
  2. Oggetti e funzioni creati durante il render sono nuovi ogni volta. { id } o () => {} non è mai uguale al precedente secondo Object.is, quindi riesegue l'effect a ogni render. Spostali dentro l'effect, fuori dal componente, oppure memoizzali.
  3. Dipendi da valori primitivi quando puoi. [user.id] è più stabile di [user] quando conta solo l'ID.
  4. Usa funzioni di aggiornamento per eliminare dipendenze dallo stato. setCount(c => c + 1) non legge count, quindi non serve elencare count.
  5. Non zittire il linter per "sistemare" un ciclo. Un ciclo infinito di solito significa che l'effect imposta uno stato che è anche una dipendenza, oppure dipende da un oggetto nuovo. Correggi la causa. Per i valori che vuoi leggere senza reagire ai loro cambiamenti, usa useEffectEvent su React 19.2+, oppure un ref che tieni aggiornato su React 18.
// Stale closure: logs 0 forever
useEffect(() => {
  const id = setInterval(() => console.log(count), 1000);
  return () => clearInterval(id);
}, []); // count is missing

// Fixed: re-subscribe when count changes
useEffect(() => {
  const id = setInterval(() => console.log(count), 1000);
  return () => clearInterval(id);
}, [count]);

// Fetch with cleanup so a slow old response cannot overwrite a newer one
useEffect(() => {
  const controller = new AbortController();
  fetch(`/api/users/${userId}`, { signal: controller.signal })
    .then((r) => r.json())
    .then(setUser)
    .catch((err) => { if (err.name !== "AbortError") setError(err); });
  return () => controller.abort();
}, [userId]);

12 custom hook da copiare nel tuo progetto

Un custom hook è una funzione il cui nome inizia con use e che chiama altri hook. Permette a più componenti di condividere logica con stato; ogni componente che lo chiama ottiene il proprio stato indipendente. Gli hook qui sotto sono scritti per questa pagina in TypeScript, funzionano su React 18 e 19 e non hanno dipendenze. Ognuno si chiude con una nota su quando non usarlo.

1. useDebounce

Restituisce un valore che si aggiorna solo dopo che l'input ha smesso di cambiare per delay millisecondi. Ottimo per i campi di ricerca che chiamano un'API.

import { useEffect, useState } from "react";

export function useDebounce<T>(value: T, delay = 300): T {
  const [debounced, setDebounced] = useState(value);
  useEffect(() => {
    const id = window.setTimeout(() => setDebounced(value), delay);
    return () => window.clearTimeout(id);
  }, [value, delay]);
  return debounced;
}

// const debouncedQuery = useDebounce(query, 400);
// useEffect(() => { search(debouncedQuery); }, [debouncedQuery]);

Quando non usarlo: Quando ti serve solo che la UI resti reattiva mentre renderizza una lista grande. È un problema di rendering, e useDeferredValue lo risolve senza un ritardo artificiale. Evitalo anche per la validazione dei form che deve sembrare istantanea.

2. useLocalStorage

Funziona come useState ma salva il valore in localStorage e lo ripristina alla visita successiva.

import { useEffect, useState } from "react";

export function useLocalStorage<T>(key: string, initial: T) {
  const [value, setValue] = useState<T>(() => {
    try {
      const raw = window.localStorage.getItem(key);
      return raw === null ? initial : (JSON.parse(raw) as T);
    } catch {
      return initial; // private mode, blocked storage or bad JSON
    }
  });

  useEffect(() => {
    try {
      window.localStorage.setItem(key, JSON.stringify(value));
    } catch {
      /* storage full or blocked: keep the in-memory value */
    }
  }, [key, value]);

  return [value, setValue] as const;
}

Quando non usarlo: Per qualsiasi dato sensibile (token, dati personali), perché qualsiasi script nella pagina può leggere localStorage. Nemmeno per le pagine renderizzate lato server senza attenzione: il server non ha window, quindi leggi lo storage dopo il mount per evitare un mismatch di hydration. Non si sincronizza tra le schede; aggiungi un listener per l'evento storage se ti serve.

3. useMediaQuery

Restituisce true finché una media query CSS corrisponde, e si aggiorna quando cambia.

import { useCallback, useSyncExternalStore } from "react";

export function useMediaQuery(query: string): boolean {
  const subscribe = useCallback((onChange: () => void) => {
    const mql = window.matchMedia(query);
    mql.addEventListener("change", onChange);
    return () => mql.removeEventListener("change", onChange);
  }, [query]);

  return useSyncExternalStore(
    subscribe,
    () => window.matchMedia(query).matches,
    () => false, // server snapshot
  );
}

// const isDesktop = useMediaQuery("(min-width: 1024px)");
// const reduceMotion = useMediaQuery("(prefers-reduced-motion: reduce)");

Quando non usarlo: Per modifiche di layout che il CSS può fare da solo. Nascondere o riorganizzare elementi con i breakpoint di Tailwind (md:hidden) funziona prima che si carichi JavaScript e non sfarfalla mai. Usa l'hook quando devi renderizzare componenti diversi o evitare del tutto di caricare qualcosa.

4. useToggle

Un booleano con una funzione toggle stabile, per menu, modali e sezioni espandibili.

import { useCallback, useState } from "react";

export function useToggle(initial = false) {
  const [on, setOn] = useState(initial);
  const toggle = useCallback(() => setOn((v) => !v), []);
  return [on, toggle, setOn] as const;
}

// const [open, toggleOpen, setOpen] = useToggle();

Quando non usarlo: Quando lo stato ha più di due valori (chiuso, in apertura, aperto, errore). Usa una union di stringhe con useState o useReducer invece di accumulare più toggle.

5. usePrevious

Restituisce il valore del render precedente, comodo per mostrare una variazione ("in crescita da 12") o reagire a un passaggio di stato.

import { useEffect, useRef } from "react";

export function usePrevious<T>(value: T): T | undefined {
  const ref = useRef<T>();
  useEffect(() => {
    ref.current = value;
  }, [value]);
  return ref.current;
}

Quando non usarlo: Per decidere se aggiornare lo stato. La documentazione di React mostra un pattern più pulito per questo: conserva il valore precedente nello stato e confrontalo durante il render. Questo hook legge un ref durante il render, cosa che oggi funziona ma è il tipo di codice che le linee guida di React chiedono di usare di rado.

6. useOnClickOutside

Chiama un handler quando l'utente preme in un punto qualsiasi fuori da un dato elemento. Tipico per chiudere un menu o un popover personalizzato.

import { useEffect, useRef, type RefObject } from "react";

export function useOnClickOutside(
  ref: RefObject<HTMLElement>,
  handler: (event: PointerEvent) => void,
) {
  const saved = useRef(handler);
  useEffect(() => { saved.current = handler; }, [handler]);

  useEffect(() => {
    const listener = (event: PointerEvent) => {
      if (!ref.current || ref.current.contains(event.target as Node)) return;
      saved.current(event);
    };
    document.addEventListener("pointerdown", listener);
    return () => document.removeEventListener("pointerdown", listener);
  }, [ref]);
}

Quando non usarlo: Se usi già una libreria headless (Radix, Headless UI) per il menu o la finestra di dialogo: gestiscono per te i clic esterni, Escape e il ritorno del focus. Un menu fatto a mano basato solo su questo hook di solito non supporta la tastiera.

7. useInterval

Un setInterval che chiama sempre la callback più recente e può essere messo in pausa passando null come ritardo.

import { useEffect, useRef } from "react";

export function useInterval(callback: () => void, delay: number | null) {
  const saved = useRef(callback);
  useEffect(() => { saved.current = callback; }, [callback]);

  useEffect(() => {
    if (delay === null) return;
    const id = window.setInterval(() => saved.current(), delay);
    return () => window.clearInterval(id);
  }, [delay]);
}

// useInterval(() => setTicks((t) => t + 1), running ? 1000 : null);

Quando non usarlo: Per le animazioni (usa CSS o requestAnimationFrame) o per orologi precisi: gli intervalli accumulano ritardi e i browser li rallentano nelle schede in background. Per un conto alla rovescia, calcola il tempo rimanente da un timestamp di fine fisso a ogni tick.

8. useWindowSize

Restituisce larghezza e altezza attuali della finestra e si aggiorna al ridimensionamento.

import { useSyncExternalStore } from "react";

function subscribe(onChange: () => void) {
  window.addEventListener("resize", onChange);
  return () => window.removeEventListener("resize", onChange);
}

export function useWindowSize() {
  const width = useSyncExternalStore(subscribe, () => window.innerWidth, () => 0);
  const height = useSyncExternalStore(subscribe, () => window.innerHeight, () => 0);
  return { width, height };
}

Quando non usarlo: Per i breakpoint: useMediaQuery ri-renderizza solo quando la query cambia esito, mentre questo ri-renderizza a ogni pixel di ridimensionamento. Per la dimensione di un singolo elemento invece della finestra, usa un ResizeObserver. (I due snapshot separati sono voluti: restituire un nuovo oggetto da un'unica funzione snapshot creerebbe un ciclo infinito.)

9. useCopyToClipboard

Copia il testo con la Clipboard API ed espone un flag copied di breve durata per il feedback del pulsante.

import { useCallback, useState } from "react";

export function useCopyToClipboard(resetAfter = 1500) {
  const [copied, setCopied] = useState(false);
  const copy = useCallback(async (text: string) => {
    try {
      await navigator.clipboard.writeText(text);
      setCopied(true);
      window.setTimeout(() => setCopied(false), resetAfter);
      return true;
    } catch {
      setCopied(false); // insecure context or permission denied
      return false;
    }
  }, [resetAfter]);
  return [copied, copy] as const;
}

Quando non usarlo: Sulle pagine servite in semplice HTTP: navigator.clipboard richiede un contesto sicuro (HTTPS o localhost). Mostra sempre un'alternativa, come selezionare il testo, quando restituisce false.

10. useFetch

Carica il JSON di un URL, tiene traccia degli stati di caricamento e di errore, e annulla la richiesta quando l'URL cambia o il componente viene smontato.

import { useEffect, useState } from "react";

type FetchState<T> = { data: T | null; error: Error | null; loading: boolean };

export function useFetch<T>(url: string | null): FetchState<T> {
  const [state, setState] = useState<FetchState<T>>({ data: null, error: null, loading: !!url });

  useEffect(() => {
    if (!url) return;
    const controller = new AbortController();
    setState((s) => ({ ...s, loading: true, error: null }));

    fetch(url, { signal: controller.signal })
      .then((res) => {
        if (!res.ok) throw new Error(`Request failed with status ${res.status}`);
        return res.json() as Promise<T>;
      })
      .then((data) => setState({ data, error: null, loading: false }))
      .catch((error: Error) => {
        if (error.name === "AbortError") return; // superseded or unmounted
        setState({ data: null, error, loading: false });
      });

    return () => controller.abort();
  }, [url]);

  return state;
}

Quando non usarlo: Nelle app più grandi. Non ha cache, deduplicazione, nuovi tentativi né rivalidazione, quindi due componenti che chiedono lo stesso URL fanno due richieste. Usa il caricamento dati del tuo framework o una libreria dati (TanStack Query, SWR) appena ti serve una di queste cose. La documentazione di React fa la stessa raccomandazione per il data fetching negli effect.

11. useEventListener

Collega un event listener alla finestra (o a qualsiasi elemento) e chiama sempre l'handler più recente, senza rinnovare l'iscrizione a ogni render.

import { useEffect, useRef } from "react";

export function useEventListener<K extends keyof WindowEventMap>(
  type: K,
  handler: (event: WindowEventMap[K]) => void,
  target: Window | HTMLElement | null = typeof window === "undefined" ? null : window,
) {
  const saved = useRef(handler);
  useEffect(() => { saved.current = handler; }, [handler]);

  useEffect(() => {
    if (!target) return;
    const listener = (event: Event) => saved.current(event as WindowEventMap[K]);
    target.addEventListener(type, listener);
    return () => target.removeEventListener(type, listener);
  }, [type, target]);
}

// useEventListener("keydown", (e) => { if (e.key === "Escape") close(); });

Quando non usarlo: Per gli eventi che React gestisce già sugli elementi che renderizzi: usa onClick, onKeyDown e simili direttamente nel JSX. Ricorri a questo hook solo per window, document o elementi di terze parti.

12. useIsMounted

Restituisce una funzione che ti dice se il componente è ancora sullo schermo, per codice async legacy che imposta lo stato dopo un await.

import { useCallback, useEffect, useRef } from "react";

export function useIsMounted() {
  const mounted = useRef(false);
  useEffect(() => {
    mounted.current = true;
    return () => { mounted.current = false; };
  }, []);
  return useCallback(() => mounted.current, []);
}

// const isMounted = useIsMounted();
// const data = await load(); if (isMounted()) setData(data);

Quando non usarlo: Come scelta predefinita. React 18 ha rimosso l'avviso sull'impostazione dello stato in componenti smontati, e la soluzione migliore è annullare il lavoro stesso (un AbortController, come in useFetch) o ignorare i risultati obsoleti con un flag nel cleanup dell'effect. Tienilo per il codice vecchio che non puoi ancora ristrutturare.

Quale hook mi serve? Un rapido elenco decisionale

Stai costruendo una libreria di componenti invece di singoli hook? Il nostro confronto tra librerie UI per React mostra gli stessi sei componenti in shadcn/ui, Radix, Headless UI, daisyUI, Flowbite e MUI, e il cheat sheet di Tailwind copre le classi utility usate nelle demo di questa pagina. Elementi React e Tailwind già pronti si trovano nella galleria di componenti.

Domande frequenti

Quanti hook integrati ha React?

Al momento della stesura (ottobre 2026, React 19.3), il riferimento degli hook di React elenca 17 hook in React stesso: useState, useReducer, useContext, useRef, useImperativeHandle, useEffect, useLayoutEffect, useInsertionEffect, useEffectEvent, useMemo, useCallback, useTransition, useDeferredValue, useDebugValue, useId, useSyncExternalStore e useActionState. React 19 aggiunge anche useOptimistic e l'API use, e React DOM aggiunge useFormStatus.

Qual è la differenza tra useMemo e useCallback?

useMemo memorizza il risultato della chiamata a una funzione, mentre useCallback memorizza la funzione stessa. useCallback(fn, deps) equivale a useMemo(() => fn, deps). Usa useMemo per valori costosi e useCallback per funzioni passate a figli memoizzati o usate come dipendenze degli effect.

Perché il mio useEffect gira due volte?

In sviluppo con lo Strict Mode attivo, React monta ogni componente, lo smonta e lo monta di nuovo per aiutarti a trovare gli effect senza cleanup. Non succede in una build di produzione. Se la doppia esecuzione causa un bug, aggiungi una funzione di cleanup che annulli ciò che l'effect ha avviato.

Cosa significa un array di dipendenze vuoto in useEffect?

Un array vuoto dice a React che l'effect non legge props né stato, quindi gira una volta dopo il mount del componente e il suo cleanup gira all'unmount. Se l'effect legge props o stato, elencali, altrimenti continua a usare i valori del primo render.

Posso usare hook di React 19 come use o useActionState in React 18?

No. use, useActionState, useOptimistic e useFormStatus non sono esportati da React 18, e useEffectEvent richiede React 19.2 o successivi. Su React 18 puoi ottenere un comportamento simile con useState più gli event handler per i form, e con un ref che contiene la callback più recente al posto di useEffectEvent.

Cosa rende una funzione un custom hook?

Il suo nome inizia con use e chiama almeno un altro hook. Questa convenzione di nome permette alle regole di lint di React di verificare che rispetti le regole degli hook. Ogni componente che chiama un custom hook ottiene il proprio stato separato; gli hook condividono la logica, non i dati.

Dal vivo: {title}

+3 (updater)

Azzera

Leggi la sezione su useReducer

Aggiungi una tua voce

Nuova voce

Nuova attività

Rimuovi completate

Leggo "{theme}" dal context

Cambia il valore del provider

Clicca il pulsante per darmi il focus

Elemento da mettere a fuoco

Dai il focus all'input

Aggiungi al ref (nessun render)

Ri-renderizza

Valore del ref mostrato all'ultimo render: {n}

Scrivi qui

Input del figlio

Il genitore chiama shake()

Il genitore chiama clear()

effect eseguito: intervallo avviato

cleanup eseguito: intervallo cancellato

Pausa

Riprendi

Ridimensionami scrivendo

Testo da misurare

Misurato prima del paint: largo {n}px

Colore del bordo

Questa regola è stata inserita nell'head della pagina

Filtra 4,000 elementi

Filtra elementi

Render non correlato ({n})

{n} corrispondenze. Il filtro è stato eseguito {runs} volte (i render non correlati non lo eseguono).

{label} (figlio renderizzato {n} volte)

funzione inline

Ri-renderizza il genitore

Conteggio: {n}

Chi siamo

Lista lenta

Contatti

Cambio in corso (la vecchia scheda resta cliccabile)...

Visualizzato: {tab}

Riga

Contenuto della scheda {tab}.

Risultati renderizzati per: "{query}"

Scrivi veloce: l'input resta reattivo

Cerca

Nome

Email

Rete:

Larghezza della finestra:

Ridimensiona la finestra o disattiva la rete per vederlo aggiornarsi.

Questo componente chiama un custom hook che si etichetta come "{label}" in React DevTools. Apri la scheda Components per vedere l'etichetta accanto all'hook.

Online

Offline

array di dipendenze

[nessun array] eseguito (render {n})

[] eseguito una volta al mount

[a] eseguito, a = {n}

Cambia a ({n})

Cambia b ({n})

Svuota il log

Scrivi, poi fermati per mezzo secondo

Input con debounce

Immediato: "{text}" · Con debounce: "{debounced}"

Scrivi, poi ricarica la pagina

Nome salvato

Salvato solo in questo browser.

Salvato solo in questo browser: "{name}".

useMediaQuery: il viewport è {size}, il tema di sistema è {theme}.

768px o più largo

più stretto di 768px

Attivo

Disattivo

Conteggio {n}

il precedente era {value}

ancora niente

Ferma

Avvia

{n} tick

Copiato

Copia un comando

Apri menu

Clicca in un punto qualsiasi fuori dal menu per chiuderlo.

Inizia gratis · Nessuna carta di credito richiesta

Correlati: Guide

Prodotto

Per chi è

Funzionalità

Risorse

Azienda

Mappa del sito