React

useImperativeHandle: esporre un’API, non un ref

useImperativeHandle sostituisce il valore di un ref con un oggetto che scegli tu: il genitore chiama metodi espliciti e non riceve il nodo DOM.

In sintesi

  • useImperativeHandle cambia il contenuto del ref, non il modo in cui viene passato.
  • Esporre due metodi al posto del nodo riduce la superficie pubblica del componente e il numero di accoppiamenti.
  • Da React 19 ref è una prop normale e forwardRef non serve più, mentre l’hook resta.

useImperativeHandle sostituisce il valore di un ref con un oggetto che scegli tu: il genitore chiama metodi espliciti e non riceve il nodo DOM. Conviene quando l’API pubblica deve restare più stretta di quella dell’elemento.

Cosa passa davvero in un ref

Un ref normale porta all’elemento: chi lo riceve può chiamare focus, rimuoverlo dal DOM, riscriverne gli attributi. Quel potere è più ampio di quanto il componente voglia concedere, e ogni metodo usato dal genitore diventa un accoppiamento che nessun tipo controlla.

Due metodi, per scelta

Un campo di testo che espone la selezione senza consegnare l’input:

const TextField = forwardRef(function TextField({ label }, ref) {
  const inputRef = useRef(null);

  // The parent gets focus and selectAll, nothing else.
  useImperativeHandle(ref, () => ({
    focus: () => inputRef.current?.focus(),
    selectAll: () => inputRef.current?.select(),
  }), []);

  return (
    
      {label}
      
    
  );
});

Il genitore riceve due funzioni. Non può leggere il valore, non può cambiare il nodo, e il tipo del ref può dichiarare esattamente quei due metodi invece di HTMLInputElement.

Con React 19 ref è una prop

Dalla versione 19 ref arriva fra le props, quindi forwardRef non serve più. Il corpo dell’hook resta identico e il chiamante non cambia una riga.

function TextField({ label, ref }) {
  const inputRef = useRef(null);

  useImperativeHandle(ref, () => ({
    focus: () => inputRef.current?.focus(),
    selectAll: () => inputRef.current?.select(),
  }), []);

  return (
    
      {label}
      
    
  );
}

Quando è la scelta sbagliata

Se il genitore deve solo misurare l’elemento, un ref normale basta. Se deve reagire a un evento, una callback nelle props è più chiara. useImperativeHandle aggiunge un livello: si usa quando l’API deve essere più stretta del nodo, non per nascondere un ref che nessuno dovrebbe avere.

Il comando

Il pacchetto è su npm; il secondo comando stampa la versione risolta, così il primo esempio non gira su una major vecchia.

npm install react@19 react-dom@19
npm ls react

Tre record pubblicati che espongono un’API ristretta: otp-input concentra il focus su una casella, dialog apre e chiude senza consegnare il portale, expandable-tabs cambia scheda da fuori. Sono record fra i 111.985 componenti del catalogo.

Fonte e licenza

React è distribuito con licenza MIT. La pagina di riferimento su react.dev descrive useImperativeHandle come via di fuga e chiede di preferire la via dichiarativa quando esiste: quel consiglio è la regola di scelta, non un dettaglio.

La limitazione

Il valore del ref viene riassegnato al commit, non durante il render: un genitore che legge ref.current nel proprio render vede l’oggetto precedente, o null al primo passaggio. L’hook non risolve quella tempistica.

Dove continuare

Il confronto diretto fra ref e stato è in ref DOM vs stato; le regole di chiamata degli hook sono in regole degli hook; il caso in cui il ref attraversa l’albero è in portal per i modali.

Domande frequenti

useImperativeHandle sostituisce forwardRef?

No, lo completa: forwardRef inoltra il ref al componente, useImperativeHandle decide cosa quel ref contiene. Da React 19 il primo pezzo diventa una prop e resta solo l’hook.

Perché non passare direttamente il nodo DOM?

Perché il nodo espone decine di metodi e proprietà: chi lo riceve può smontarlo o riscriverne gli attributi. Un oggetto con due funzioni rende l’API pubblica piccola e verificabile.

Le funzioni esposte restano stabili fra i render?

Solo se il terzo argomento dell’hook non cambia. Con un array di dipendenze vuoto l’oggetto viene ricreato una volta; con dipendenze variabili cambia identità a ogni render.

Funziona anche in un componente classe?

No. In una classe si usa un ref oggetto e si assegnano i metodi in componentDidMount; l’hook vive solo nelle funzioni.

Serve un test per un’API imperativa?

Sì, un test che chiama il metodo e controlla il risultato finale, non l’oggetto: con Testing Library si chiama ref.current.focus() e si verifica document.activeElement.