React
Progettare un custom hook
Un custom hook è una funzione che inizia con use e restituisce solo ciò che serve: comportamento, non markup, con una firma che regge la crescita.
In sintesi
- Un custom hook incapsula comportamento e restituisce dati e azioni, mai markup.
- La firma ideale ha un ingresso, un ritardo predefinito e un solo valore di ritorno.
- Oltre tre argomenti conviene un oggetto di opzioni.
- renderHook di Testing Library prova il hook senza montare l’interfaccia.
Un custom hook è una funzione che inizia con use, chiama altri hook e restituisce solo ciò che il chiamante usa: se il nome descrive un comportamento e la firma non espone i dettagli interni, l’hook è progettato bene.
Comportamento, non interfaccia
Un hook non disegna niente: legge, calcola, si iscrive a eventi e restituisce dati e azioni. Il markup resta nel componente, che è l’unico punto in cui si decide dove va un bottone e quale testo mostra.
Una firma che regge
import { useEffect, useState } from "react";
export function useDebouncedValue(value: T, delay = 300) {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const id = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(id);
}, [value, delay]);
return debounced;
}
Tre scelte rendono la firma stabile: un solo argomento di ingresso, un ritardo con valore predefinito, un solo valore di ritorno. Il chiamante non può impostare lo stato interno, quindi il comportamento resta uno e testabile.
Cosa restituire
- Oggetto quando i campi sono tre o più: i nomi rendono leggibile il punto di chiamata e l’ordine smette di contare.
- Tupla quando sono due, come fa
useState: il primo nome lo scegli tu, il secondo è l’azione. - Funzioni stabili: se restituisci una callback usata in una lista di dipendenze, avvolgila in
useCallback.
Gli errori che si pagano
- Restituire JSX: il hook diventa un componente mascherato e non si prova senza montare l’interfaccia.
- Nove parametri: oltre tre argomenti conviene un oggetto di opzioni, altrimenti il punto di chiamata diventa illeggibile.
- Nascondere un fetch: un hook che scarica dati senza dirlo nel nome crea richieste che nessuno collega alla pagina.
Testare l’hook da solo
import { renderHook, act } from "@testing-library/react";
import { useDebouncedValue } from "./use-debounced-value";
test("returns the last value after the delay", () => {
vi.useFakeTimers();
const { result, rerender } = renderHook(({ v }) => useDebouncedValue(v), {
initialProps: { v: "a" },
});
rerender({ v: "ab" });
act(() => vi.advanceTimersByTime(300));
expect(result.current).toBe("ab");
});
renderHook monta il hook senza markup: il test misura il comportamento, non il DOM. Serve act per far girare i timer dentro il ciclo di React, altrimenti l’asserzione arriva prima dell’aggiornamento.
Il comando
npm install --save-dev vitest @testing-library/react jsdom
npx vitest run
Vitest e Testing Library sono distribuiti con licenza MIT; il test gira in jsdom, che simula il DOM senza aprire un browser.
Record vivi nel catalogo
Tre record mostrano la stessa idea: use-image-upload incapsula caricamento e anteprima, multi-step-form tiene il passo corrente fuori dai singoli campi, e la categoria input raccoglie i campi che quegli hook comandano.
La limitazione
Un hook personalizzato non condivide lo stato fra due chiamanti: ogni componente ottiene la sua istanza. Per uno stato condiviso serve un context o uno store esterno; l’estrazione riduce il codice duplicato, non sostituisce un gestore di stato.
Dove continuare
Le regole che valgono anche qui sono in regole degli hook; cosa può restare derivato invece che nello stato è in stato derivato; la stessa idea in Vue è nei composables; il confronto fra i framework è in Vue 3 vs React.
Domande frequenti
Che differenza c’è fra un custom hook e una funzione normale?
Un custom hook può chiamare altri hook, quindi usare stato ed effetti; una funzione normale no. Il prefisso use lo dice al linter e a chi legge.
Un custom hook deve restituire uno stato?
No. Può restituire un valore calcolato, una funzione o un oggetto con dati e azioni: conta che la firma descriva il comportamento.
Quanti argomenti può avere un custom hook?
Tecnicamente quanti vuoi, in pratica oltre tre conviene un oggetto di opzioni: il call site resta leggibile e aggiungere un campo non rompe l’ordine.
Posso testare un custom hook senza browser?
Sì, con renderHook di Testing Library e un ambiente jsdom. Il test controlla il valore restituito e le funzioni esposte, non il markup.
Perché non restituire JSX da un hook?
Perché il hook diventerebbe un componente con un nome che inganna: React non lo monta come tale e il test deve ricostruire l’interfaccia.