React

Le regole degli hook, spiegate con i casi che rompono

Gli hook si chiamano solo al livello superiore di un componente o di un hook: se l’ordine cambia fra due render, React legge lo stato sbagliato.

In sintesi

  • Le regole degli hook riguardano l’ordine delle chiamate, non il nome della funzione.
  • Una chiamata saltata da un return anticipato sposta tutti gli hook successivi di uno slot.
  • Il plugin ufficiale ESLint è distribuito con licenza MIT e segnala i tre casi più comuni.

Le regole degli hook sono due: chiamali solo al livello superiore del componente, e solo dentro funzioni React. Condizioni, cicli e callback annidate rompono l’ordine su cui React costruisce lo stato.

Perché l’ordine conta

React non salva lo stato per nome. Ogni componente porta una lista ordinata di slot: il primo useState occupa lo slot zero, il secondo lo slot uno, e al render successivo React rilegge la lista nella stessa sequenza. Se una condizione salta una chiamata, tutti gli hook dopo di lei scivolano di una posizione e leggono il valore di un altro.

Il caso che rompe più spesso

Un’uscita anticipata prima di un hook è il modo più comune di violare la regola senza accorgersene: il primo render non arriva alla chiamata, il secondo sì.

function Profile({ user, loading }) {
  const [open, setOpen] = useState(false);

  if (loading) return Loading;      // first render stops here

  const [tab, setTab] = useState("info");  // slot 1 appears only later

  return (
    
       setOpen(!open)}>{user.name}
      {open && }
    
  );
}

Sposta il return dopo tutti gli hook, oppure estrai la parte condizionale in un componente figlio: il figlio viene montato solo quando serve, e i suoi hook partono con lui, in ordine.

Gli altri tre casi

  • Dentro un ciclo: un hook in un map viene chiamato un numero di volte che dipende dai dati, quindi la lista degli slot cambia con loro.
  • Dentro una callback: una funzione passata a onClick non è un componente, e React non ha un posto dove mettere lo slot.
  • Dentro una classe: gli hook non funzionano nei metodi; quei componenti restano su this.state o vanno riscritti come funzioni.

Il controllo automatico

Il plugin ESLint ufficiale riconosce i tre casi e li segnala durante il lint. Si installa come dipendenza di sviluppo e si attiva in una configurazione flat:

npm install --save-dev eslint eslint-plugin-react-hooks
import reactHooks from "eslint-plugin-react-hooks";

export default [
  {
    files: ["src/**/*.{js,jsx,ts,tsx}"],
    plugins: { "react-hooks": reactHooks },
    rules: reactHooks.configs.recommended.rules,
  },
];

Con il file eslint.config.js nella radice, npx eslint src blocca il merge se qualcuno rimette un hook dentro un if. Il plugin è distribuito con licenza MIT, come React.

Le stesse regole dentro un custom hook

Un custom hook è una funzione che inizia con use e chiama altri hook: le regole valgono identiche, perché React non vede il confine della funzione. Il prefisso non è decorazione, è il segnale che il linter usa per sapere che quella funzione può contenere hook.

Per questo un custom hook non si chiama dentro un if del componente che lo usa: la condizione deve stare al suo interno, dove il chiamante non deve saperne nulla.

Nel catalogo ogni componente React usa hook nei propri file: list-item per una riga di lista, expandable-tabs per lo stato di una tab. Entrambi sono record pubblicati tra i 111.985 componenti e i 1.556 blocchi.

La limitazione

Le regole garantiscono l’ordine delle chiamate, non la correttezza delle dipendenze: un useEffect con una lista sbagliata passa il linter e continua a leggere valori vecchi. Quel controllo è una regola separata e resta un avviso, non una prova.

Dove continuare

La differenza fra i due effect è in useEffect vs useLayoutEffect; cosa cambia nella versione 19 è in React 19; il confronto con l’altro framework diffuso è in Vue 3 vs React.

Domande frequenti

Perché React richiede un ordine fisso negli hook?

Perché lo stato è salvato in una lista di slot per componente, non per nome. React rilegge la lista nell’ordine di chiamata: se l’ordine cambia, ogni hook legge il valore di un altro slot.

Posso chiamare un hook dentro un if se la condizione non cambia mai?

No. La regola è statica e il linter non può dimostrare che la condizione resta stabile: quel codice funziona finché qualcuno non cambia la condizione, e allora rompe in produzione.

Un custom hook deve iniziare con use?

Sì, è la convenzione che il linter usa per riconoscere una funzione che può contenere hook. Senza prefisso il plugin non controlla il corpo e i chiamanti non ricevono avvisi.

Le regole valgono anche per useMemo e useCallback?

Sì. Ogni hook consuma uno slot, quindi valgono per tutti: useState, useEffect, useMemo, useCallback, useRef e i custom hook scritti sopra di loro.

Cosa succede se violo le regole?

Nei casi netti React lancia un errore, di solito “Rendered more hooks than during the previous render”. Nei casi ambigui non lancia nulla e mostra uno stato sbagliato, che è il guasto peggiore da diagnosticare.