Un React Fragment permette a un componente di restituire più elementi senza racchiuderli in un <div> in più. Scrivi <> prima del primo elemento e </> dopo l'ultimo, e React mette gli elementi direttamente nel genitore, senza alcun nodo proprio nella pagina.
Ogni Greeting restituisce un titolo e un paragrafo uno accanto all'altro. Elimina le righe <> e </> e l'anteprima mostra invece un errore di build, perché ora la funzione prova a restituire due valori.
Perché un componente ha bisogno di una sola radice
JSX è un modo più comodo di scrivere chiamate di funzione. <h2>Hi</h2> viene compilato più o meno in jsx('h2', { children: 'Hi' }), un'unica espressione. Un'istruzione return può restituire un solo valore, quindi due tag fratelli senza nulla attorno sono come scrivere return a b: il compilatore si ferma con "Adjacent JSX elements must be wrapped in an enclosing tag".
Prima che esistessero i fragment, la soluzione era un <div> contenitore. Funziona, ma il div è un elemento reale. Compare nel DOM, partecipa al layout CSS, e alcuni genitori non lo accettano proprio come figlio. Un fragment soddisfa la regola della radice unica e poi sparisce.
Puoi vedere che non aggiunge nulla. L'esempio qui sotto renderizza gli stessi due elementi attraverso un fragment e attraverso un div, poi conta i figli diretti di ciascun contenitore.
La console stampa 2 per il fragment, i due span, e 1 per il div, che li racchiude. In un contenitore flex o grid questa differenza decide se gli span vengono disposti come elementi o raggruppati in uno solo.
Sintassi breve e componente Fragment
Ci sono due modi di scrivere un fragment, e vengono compilati nella stessa cosa:
import { Fragment } from 'react';
// Short syntax, no import needed
<>
<dt>React</dt>
<dd>A library for building user interfaces</dd>
</>
// Long form
<Fragment>
<dt>React</dt>
<dd>A library for building user interfaces</dd>
</Fragment>
Usa <>...</> quasi ovunque. Ricorri a <Fragment> in un solo caso: quando il fragment ha bisogno di una key. La sintassi breve non accetta attributi, quindi <key={id}> non è JSX valido. Nel codice più vecchio potresti vedere anche <React.Fragment>; è lo stesso componente raggiunto tramite l'import di default React.
Fragment con key nelle liste
Quando renderizzi una lista con map, ogni elemento ha bisogno di una key perché React possa tracciarlo tra un rendering e l'altro (la pagina su liste e key spiega perché). Se ogni elemento è un gruppo di elementi invece di uno solo, la key va sul fragment, quindi ti serve la forma lunga.
Una lista di definizioni è il caso classico: ogni voce è un <dt> e un <dd>, e un <dl> dovrebbe contenerli direttamente.
Clicca Reverse e le coppie si scambiano di posto come unità intere, perché ogni coppia ha la sua key. Anche un <div> attorno a ogni coppia accetterebbe una key, ed è HTML valido dentro un dl, ma aggiunge al markup un terzo livello che un fragment tiene fuori.
Fragment dentro le tabelle
Le tabelle sono il punto in cui un contenitore di troppo fa più danni. Un <tr> si aspetta celle <td> e <th> come figli diretti. Se un componente che restituisce più celle le racchiude in un <div>, il browser tratta il div come una strana cella unica e le celle al suo interno smettono di allinearsi con l'intestazione.
La riga con il fragment ha il prezzo sotto Price e la disponibilità sotto Stock. Nella riga con il div entrambe le celle sono schiacciate nella colonna Price e la colonna Stock è vuota. In sviluppo React registra anche un avviso che un <div> non può essere figlio di un <tr>; l'anteprima gira come una build di produzione, quindi salta quel controllo, e ciò che vedi qui è il layout rotto. Con il rendering lato server lo stesso errore può causare anche errori di hydration, perché il parser HTML del browser sposta il div fuori posto.
La stessa regola vale per <ul> e <ol>, che dovrebbero contenere elementi <li>, e per <select>, che dovrebbe contenere elementi <option>. Ogni volta che un componente restituisce pezzi che vanno direttamente dentro un genitore di questo tipo, restituisci un fragment.
Fragment o array
Un componente può anche restituire un array di elementi, e React li renderizza in ordine come un fragment. Ogni elemento di un array restituito ha bisogno della sua key, proprio come l'output di map, perché React tratta l'array come una lista:
function Cells() {
return [<td key="price">$4</td>, <td key="stock">In stock</td>];
}
Funziona, ma è più rumoroso: virgole tra gli elementi, una key su ognuno e una parentesi quadra facile da perdere di vista quando il markup cresce. Usa un fragment per un gruppo fisso di elementi, e tieni gli array per ciò che sono per natura, il risultato di un map sui dati.
Cosa un fragment non può fare
Poiché un fragment non renderizza alcun elemento, non ha dove mettere una classe, uno stile, un gestore di eventi o un ref. <Fragment className="row"> non fa nulla di utile, e l'unica prop che React legge da un fragment è key. Quando devi dare uno stile al gruppo o ascoltare i clic su di esso, ti serve dopotutto un elemento reale, e va benissimo: scegline uno valido dove si trova, come un <tbody> per raggruppare righe o un <li> per contenere un elemento di lista.
Un fragment funziona ovunque funzioni un elemento, anche dentro il rendering condizionale: {show && <>...</>} mostra o nasconde l'intero gruppo in una volta.
Domande frequenti
Cos'è un Fragment in React?
Un Fragment è un contenitore che permette a un componente di restituire più elementi come un unico valore senza aggiungere un nodo DOM. <><h1 /><p /></> mette l'h1 e il p direttamente nel genitore.
Qual è la differenza tra <> e <Fragment>?
Producono la stessa cosa. <>...</> è la sintassi breve. <Fragment> è la forma lunga che importi da react, ed è l'unica che può ricevere una key, che ti serve quando renderizzi fragment in una lista.
Perché usare un Fragment invece di un div?
Un div è un elemento reale. Può rompere i layout che si aspettano figli diretti (contenitori flex e grid, ul, table, tr) e aggiunge rumore al DOM. Un Fragment non aggiunge nulla.
Un Fragment può avere className o onClick?
No. Un Fragment non renderizza alcun elemento, quindi non c'è nulla su cui mettere una classe, uno stile o un gestore di eventi. L'unica prop che accetta è key. Se ti serve una di queste cose, usa un elemento reale.
Perché ricevo "Adjacent JSX elements must be wrapped in an enclosing tag"?
Un componente ha restituito due elementi fratelli senza nulla attorno. JSX viene compilato in un'unica espressione, quindi racchiudi i fratelli in <>...</>.