Un hook personalizzato è una funzione il cui nome inizia con use e che chiama altri hook. Ne scrivi uno quando due componenti hanno bisogno della stessa logica con stato: sposta le chiamate a useState e useEffect nella funzione, restituisci ciò che serve al componente e chiamala da ogni componente.
Entrambi gli spoiler chiamano useToggle, e aprirne uno lascia chiuso l'altro. Ogni chiamata esegue il proprio useState, quindi ogni componente riceve il proprio valore. Un hook personalizzato condivide la logica, non lo stato.
Le regole per scriverne uno
- Il nome inizia con
usee una lettera maiuscola:useToggle,useOnlineStatus. La regola di lint per gli hook usa il prefisso per trovare le chiamate agli hook e controllarle. - Chiama almeno un hook. Una funzione di supporto che non chiama hook è una funzione normale; chiamala
formatDate, nonuseFormatDate, così può essere chiamata ovunque, anche dentro le condizioni. - Segue le regole degli hook: chiama gli hook al suo livello più alto, e chiamalo solo da componenti o da altri hook personalizzati.
- Restituisce ciò che è utile. Un array (
[on, toggle]) è comodo quando chi lo chiama rinomina i valori; un oggetto ({ data, loading, error }) si legge meglio da tre valori in su.
useLocalStorage: uno stato che sopravvive al ricaricamento
useState dimentica tutto al ricaricamento. Questo hook legge il valore iniziale da localStorage e riscrive lì ogni modifica.
L'anteprima gira in un frame isolato in cui il browser blocca localStorage, quindi qui vedrai l'avviso nella Console e l'hook ripiega su un semplice stato. In una pagina normale il nome sopravvive al ricaricamento. Il try/catch non serve solo per questa anteprima: navigazione privata, cookie bloccati e una quota piena fanno tutti generare errori allo storage anche nelle app reali. Passare una funzione a useState significa che lo storage viene letto una sola volta, al primo rendering.
useDebounce: aspettare che l'utente smetta di scrivere
Una casella di ricerca non dovrebbe lanciare una richiesta a ogni tasto. useDebounce restituisce una copia di un valore che si aggiorna solo dopo che ha smesso di cambiare per un certo ritardo.
Scrivi una parola velocemente: Typed segue ogni tasto, Debounced si allinea mezzo secondo dopo che ti sei fermato, e la Console registra una sola ricerca. Ogni tasto cambia value, quindi React esegue la funzione di cleanup (clearTimeout) prima di avviare un nuovo timer. Cambia 500 in 0 e ogni tasto registra la propria ricerca.
useWindowWidth: iscriversi con una funzione di cleanup
Un hook che ascolta il browser deve rimuovere il proprio listener quando il componente sparisce, altrimenti ogni montaggio ne lascia uno indietro.
Ridimensiona la finestra del browser e il numero la segue. Clicca il pulsante e la Console mostra il listener che viene rimosso. Elimina il return dall'effetto, poi monta e smonta qualche volta: ottieni ogni volta una riga added e nessuna removed, quindi i vecchi listener si accumulano. Per valori del browser come questo, useSyncExternalStore è l'hook che React ha creato apposta, ed è un'ottima cosa da chiamare dentro un hook personalizzato.
La logica è condivisa, lo stato no
Due componenti che chiamano lo stesso hook ricevono due copie separate di tutto ciò che contiene. Questo sorprende chi si aspetta che un hook funzioni come una variabile globale.
Clicca uno dei due pulsanti e si muove solo quel contatore. Se entrambi devono mostrare lo stesso numero, sposta lo stato in App e passalo verso il basso, oppure mettilo nel context. Un hook personalizzato può racchiudere quello stato condiviso per comodità (useCart() che legge un CartContext), ma la condivisione arriva dal context, non dall'hook.
Quando scrivere un hook personalizzato
Estrai un hook quando lo stesso gruppo di chiamate agli hook compare in due componenti, o quando la logica degli effetti di un componente è abbastanza lunga che un nome la spiegherebbe meglio (useOnlineStatus() si legge meglio di venti righe di listener). Non racchiudere una singola chiamata a useState in un hook solo per rinominarla, e non scrivere hook che chiamano solo useEffect per impostare lo stato dalle props; calcola invece quel valore durante il rendering.
Domande frequenti
Cos'è un hook personalizzato in React?
Una funzione JavaScript il cui nome inizia con use e che chiama uno o più altri hook. Racchiude logica con stato (stato, effetti, ref) così qualsiasi componente può riutilizzarla chiamando la funzione.
Gli hook personalizzati condividono lo stato tra componenti?
No. Ogni chiamata a un hook personalizzato esegue le proprie chiamate a useState e useEffect, quindi ogni componente riceve uno stato indipendente. Per condividere lo stesso valore tra componenti, solleva lo stato oppure mettilo nel context o in uno store esterno.
Perché il nome di un hook personalizzato deve iniziare con use?
Il nome dice alle regole di lint di React e agli altri sviluppatori che la funzione chiama degli hook, quindi le si applicano le regole degli hook: chiamala al livello più alto, mai dentro una condizione o un ciclo. Senza il prefisso, il linter non può controllare le chiamate al suo interno.
Un hook personalizzato può restituire JSX?
Può restituire qualsiasi cosa, ma se restituisce JSX di solito è meglio scriverlo come componente. Gli hook personalizzati restituiscono tipicamente valori e funzioni, come array ([value, toggle]) o come oggetto ({ data, error, loading }).
Posso chiamare un hook personalizzato dentro useEffect o un gestore di eventi?
No. Un hook personalizzato è comunque un hook, quindi segue le regole degli hook: chiamalo al livello più alto di un componente o di un altro hook personalizzato.