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.

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.