React

Render props vs hook: quando vince quale

Vince il hook quando serve solo stato e comportamento; vince la render prop quando il markup deve restare al componente che lo ospita.

In sintesi

  • Un hook restituisce valori e funzioni, una render prop restituisce JSX: la seconda decide anche la struttura.
  • La render prop resta necessaria quando la libreria possiede misure, assi di un grafico o maniglie di trascinamento.
  • Una funzione inline in una render prop cambia identità a ogni render e annulla il confronto di memo.

Vince il hook nella maggior parte dei casi, perché restituisce stato e funzioni senza imporre markup; vince la render prop quando la struttura deve restare al componente che la ospita, come in una lista virtualizzata o in un grafico.

Cosa restituisce ciascuno

Il hook restituisce dati e callback, e il chiamante scrive il JSX. La render prop riceve una funzione che il componente chiama con i suoi dati, e quella funzione restituisce il JSX: il controllo della struttura passa dall’altra parte.

function useHover() {
  const [hovered, setHovered] = useState(false);
  const bind = {
    onMouseEnter: () => setHovered(true),
    onMouseLeave: () => setHovered(false),
  };
  return { hovered, bind };
}
function Hover({ children }) {
  const [hovered, setHovered] = useState(false);
  const bind = {
    onMouseEnter: () => setHovered(true),
    onMouseLeave: () => setHovered(false),
  };
  return children({ hovered, bind });
}

La differenza in una tabella

ServeHookRender prop
Stato riutilizzabileSì, valore di ritornoSì, argomento della funzione
Markup deciso dalla libreriaNoSì
Un livello in più nell’alberoNoSì, un componente
Test in isolamentorenderHookrender con una funzione

Quando la render prop è l’unica scelta

Quando la libreria possiede le misure: una lista virtualizzata conosce l’altezza delle righe, un grafico conosce gli assi, un drag and drop conosce le maniglie. Il chiamante non può riscrivere quel markup, quindi la funzione che riceve i dati è l’unico contratto sensato.

Perché il hook ha vinto nei casi normali

Un hook non aggiunge un componente all’albero, non richiede una funzione nuova a ogni render e si compone con altri hook. Il tipo di ritorno si legge nella firma, e il test si scrive con renderHook senza montare nessun markup.

L’errore che costa un render

Una funzione freccia scritta dentro il JSX cambia identità a ogni passaggio: se il componente è avvolto in memo, il confronto sulle props fallisce e il sottoalbero si ridisegna. Con il hook lo stesso problema esiste sulle dipendenze, ma si vede in un punto solo.

Il comando

Per verificare entrambe le forme serve un test runner e una libreria di rendering; il comando installa i due pacchetti e lancia la suite.

npm install --save-dev vitest @testing-library/react
npx vitest run

Il catalogo contiene entrambe le forme: use-mouse-position-ref è un hook pubblicato come record, spotlight-card decide da sé il markup e riceve solo i contenuti, container-scroll-animation possiede la misura dello scroll. Sono record fra i 111.985 componenti.

Fonte e licenza

React e @testing-library/react sono distribuiti con licenza MIT. La documentazione React tiene ancora una pagina dedicata alle render props proprio perché il pattern non è sparito: gli hook coprono i casi in cui il markup non è in gioco.

La limitazione

Un hook non può restituire JSX, quindi una libreria che deve possedere il markup non può essere un hook: chi espone entrambe le forme costringe il chiamante a imparare due contratti e a scegliere senza un criterio unico.

Dove continuare

Le regole di chiamata sono in regole degli hook; quando derivare lo stato evita del tutto l’effetto è in stato derivato; il confronto con i composable di Vue è in Composition API di Vue 3.

Domande frequenti

Posso usare hook e render props nello stesso componente?

Sì, e in molti casi conviene: il hook gestisce lo stato, la render prop decide dove finisce il markup. L’importante è che la funzione passata sia stabile, altrimenti ogni render ne crea una nuova.

Le render props sono deprecate?

No. React non le ha deprecate: le ha superate nei casi comuni, perché un hook fa la stessa cosa senza aggiungere un livello all’albero.

Perché il hook non può restituire JSX?

Perché un hook è una funzione di dati, non di rendering: React lo chiama durante il render del componente e il JSX deve nascere da un componente. Restituire JSX da un hook confonde i due ruoli.

Il test con renderHook basta?

Per lo stato e le callback sì. Se il componente che usa il hook ha un markup critico serve anche un test con Testing Library, perché il hook non vede il DOM.

Meglio children o render prop?

children quando il contenuto è statico e il componente lo colloca; render prop quando il contenuto dipende da dati che il componente calcola, come l’indice di una riga o lo stato di hover.