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.
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.
| Hook | Gruppo | Serve a | Da |
|---|---|---|---|
| useState | Stato | conservare un valore che fa ri-renderizzare il componente quando cambia | React 18 |
| useReducer | Stato | gestire uno stato con molti aggiornamenti correlati tramite un'unica funzione reducer | React 18 |
| useContext | Contesto | leggere un valore dal provider più vicino sopra il componente | React 18 |
| useRef | Ref | conservare un valore mutabile o un nodo DOM che non provoca render | React 18 |
| useImperativeHandle | Ref | scegliere quali metodi un genitore può chiamare tramite un ref | React 18 |
| useEffect | Effetti | sincronizzarsi con qualcosa fuori da React dopo il paint | React 18 |
| useLayoutEffect | Effetti | misurare il layout prima che il browser disegni | React 18 |
| useInsertionEffect | Effetti | inserire regole di stile prima che girino i layout effect (librerie CSS-in-JS) | React 18 |
| useEffectEvent | Effetti | leggere le props più recenti dentro un effect senza rieseguirlo | React 19.2 |
| useMemo | Prestazioni | memorizzare un calcolo costoso tra un render e l'altro | React 18 |
| useCallback | Prestazioni | memorizzare una funzione così i figli memoizzati non si ri-renderizzano | React 18 |
| useTransition | Prestazioni | segnare un aggiornamento di stato come non urgente e ottenere un flag di attesa | React 18 |
| useDeferredValue | Prestazioni | lasciare che una parte lenta della UI resti indietro rispetto alla digitazione veloce | React 18 |
| useId | Altri | generare ID univoci e stabili per label e attributi ARIA | React 18 |
| useSyncExternalStore | Altri | iscriversi a uno store che vive fuori da React | React 18 |
| useDebugValue | Altri | etichettare un custom hook in React DevTools | React 18 |
| useActionState | Altri | seguire il risultato e lo stato di attesa di una form action | React 19 |
| useOptimistic | Altri | mostrare il risultato atteso di un'azione prima che finisca | React 19 |
| use | API | leggere una promise o un context, anche dentro le condizioni | React 19 |
| useFormStatus | React DOM | leggere lo stato di attesa del form genitore | React 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.
useStateCosa 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" }).
useReducerCosa 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.
useContextCosa 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.
useRefCosa 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.
useImperativeHandleCosa 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.
useEffectCosa 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.
useLayoutEffectCosa 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.
useInsertionEffectCosa 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.
useEffectEventCosa 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.
useMemoCosa 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.
useCallbackCosa 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.
useTransitionCosa 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.
useDeferredValueCosa 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).
useIdCosa 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.
useSyncExternalStoreCosa 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.
useDebugValueCosa 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()).
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.useActionStateCosa 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.
useOptimisticCosa 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.
useCosa 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.
useFormStatusCosa 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.
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:
| Scrivi | L'effect gira | Uso tipico |
|---|---|---|
| useEffect(fn) | dopo ogni render | raro; 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'unmount | sottoscrizioni e setup che non usano props né stato |
| useEffect(fn, [a, b]) | dopo il mount, poi ogni volta che a o b cambia | fetch 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.
eslint-plugin-react-hooks e lascia che exhaustive-deps ti dica cosa manca.{ 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.[user.id] è più stabile di [user] quando conta solo l'ID.setCount(c => c + 1) non legge count, quindi non serve elencare count.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]);
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.
useDebounceRestituisce 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.
useLocalStorageFunziona 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.
useMediaQueryRestituisce 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.
useToggleUn 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.
usePreviousRestituisce 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.
useOnClickOutsideChiama 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.
useIntervalUn 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.
useWindowSizeRestituisce 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.)
useCopyToClipboardCopia 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.
useFetchCarica 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.
useEventListenerCollega 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.
useIsMountedRestituisce 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.
useState (o useReducer se più valori cambiano insieme).useRef.useContext, oppure uno store esterno letto con useSyncExternalStore.useEffect; passa a useLayoutEffect solo per misurazioni che causano uno sfarfallio visibile.useMemo. Un figlio memoizzato continua a ri-renderizzarsi a causa di una prop funzione: useCallback.useDeferredValue o useTransition.useActionState, useFormStatus, useOptimistic.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.
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.
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.
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.
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.
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.
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
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