Nota bene

Questa pagina è stata tradotta automaticamente e supervisionata da un maintainer. Un’ulteriore revisione da parte della community sarebbe comunque utile. Migliora questa traduzione.

Insidia

L’uso di Children è poco comune e può portare a codice fragile. Vedi le alternative comuni.

Children ti permette di manipolare e trasformare il JSX che hai ricevuto come prop children.

const mappedChildren = Children.map(children, child =>
<div className="Row">
{child}
</div>
);

Reference

Children.count(children)

Chiama Children.count(children) per contare il numero di children nella struttura dati children.

import { Children } from 'react';

function RowList({ children }) {
return (
<>
<h1>Total rows: {Children.count(children)}</h1>
...
</>
);
}

Vedi altri esempi sotto.

Parameters

  • children: Il valore della prop children ricevuto dal tuo componente.

Returns

Il numero di nodi all’interno di questi children.

Caveats

  • I nodi vuoti (null, undefined e valori booleani), le stringhe, i numeri e gli elementi React contano come nodi individuali. Gli array non contano come nodi individuali, ma i loro children sì. L’attraversamento non va più a fondo degli elementi React: non vengono renderizzati e i loro children non vengono attraversati. I Fragment non vengono attraversati.

Children.forEach(children, fn, thisArg?)

Chiama Children.forEach(children, fn, thisArg?) per eseguire del codice per ogni child nella struttura dati children.

import { Children } from 'react';

function SeparatorList({ children }) {
const result = [];
Children.forEach(children, (child, index) => {
result.push(child);
result.push(<hr key={index} />);
});
// ...

Vedi altri esempi sotto.

Parameters

  • children: Il valore della prop children ricevuto dal tuo componente.
  • fn: La funzione che vuoi eseguire per ogni child, simile al callback del metodo forEach degli array. Verrà chiamata con il child come primo argomento e il suo indice come secondo argomento. L’indice parte da 0 e si incrementa a ogni chiamata.
  • optional thisArg: Il valore di this con cui la funzione fn dovrebbe essere chiamata. Se omesso, è undefined.

Returns

Children.forEach restituisce undefined.

Caveats

  • I nodi vuoti (null, undefined e valori booleani), le stringhe, i numeri e gli elementi React contano come nodi individuali. Gli array non contano come nodi individuali, ma i loro children sì. L’attraversamento non va più a fondo degli elementi React: non vengono renderizzati e i loro children non vengono attraversati. I Fragment non vengono attraversati.

Children.map(children, fn, thisArg?)

Chiama Children.map(children, fn, thisArg?) per mappare o trasformare ogni child nella struttura dati children.

import { Children } from 'react';

function RowList({ children }) {
return (
<div className="RowList">
{Children.map(children, child =>
<div className="Row">
{child}
</div>
)}
</div>
);
}

Vedi altri esempi sotto.

Parameters

  • children: Il valore della prop children ricevuto dal tuo componente.
  • fn: La funzione di mapping, simile al callback del metodo map degli array. Verrà chiamata con il child come primo argomento e il suo indice come secondo argomento. L’indice parte da 0 e si incrementa a ogni chiamata. Devi restituire un nodo React da questa funzione. Può essere un nodo vuoto (null, undefined o un valore booleano), una stringa, un numero, un elemento React o un array di altri nodi React.
  • optional thisArg: Il valore di this con cui la funzione fn dovrebbe essere chiamata. Se omesso, è undefined.

Returns

Se children è null o undefined, restituisce lo stesso valore.

Altrimenti, restituisce un array piatto composto dai nodi che hai restituito dalla funzione fn. L’array restituito conterrà tutti i nodi che hai restituito, tranne null e undefined.

Caveats

  • I nodi vuoti (null, undefined e valori booleani), le stringhe, i numeri e gli elementi React contano come nodi individuali. Gli array non contano come nodi individuali, ma i loro children sì. L’attraversamento non va più a fondo degli elementi React: non vengono renderizzati e i loro children non vengono attraversati. I Fragment non vengono attraversati.

  • Se restituisci un elemento o un array di elementi con key da fn, le key degli elementi restituiti verranno combinate automaticamente con la key dell’elemento originale corrispondente da children. Quando restituisci più elementi da fn in un array, le loro key devono essere uniche solo localmente tra loro.


Children.only(children)

Chiama Children.only(children) per verificare che children rappresenti un singolo elemento React.

function Box({ children }) {
const element = Children.only(children);
// ...

Parameters

  • children: Il valore della prop children ricevuto dal tuo componente.

Returns

Se children è un elemento valido, restituisce quell’elemento.

Altrimenti, lancia un errore.

Caveats

  • Questo metodo lancia sempre un errore se passi un array (come il valore restituito da Children.map) come children. In altre parole, impone che children sia un singolo elemento React, non che sia un array con un singolo elemento.

Children.toArray(children)

Chiama Children.toArray(children) per creare un array dalla struttura dati children.

import { Children } from 'react';

export default function ReversedList({ children }) {
const result = Children.toArray(children);
result.reverse();
// ...

Parameters

  • children: Il valore della prop children ricevuto dal tuo componente.

Returns

Restituisce un array piatto di elementi in children.

Caveats

  • I nodi vuoti (null, undefined e valori booleani) verranno omessi nell’array restituito. Le key degli elementi restituiti verranno calcolate dalle key degli elementi originali e dal loro livello di annidamento e posizione. Questo garantisce che l’appiattimento dell’array non introduca cambiamenti nel comportamento.

Usage

Trasformare i children

Per trasformare il JSX dei children che il tuo componente riceve come prop children, chiama Children.map:

import { Children } from 'react';

function RowList({ children }) {
return (
<div className="RowList">
{Children.map(children, child =>
<div className="Row">
{child}
</div>
)}
</div>
);
}

Nell’esempio sopra, RowList avvolge ogni child che riceve in un contenitore <div className="Row">. Ad esempio, supponiamo che il componente genitore passi tre tag <p> come prop children a RowList:

<RowList>
<p>This is the first item.</p>
<p>This is the second item.</p>
<p>This is the third item.</p>
</RowList>

Poi, con l’implementazione di RowList sopra, il risultato finale renderizzato sarà simile a questo:

<div className="RowList">
<div className="Row">
<p>This is the first item.</p>
</div>
<div className="Row">
<p>This is the second item.</p>
</div>
<div className="Row">
<p>This is the third item.</p>
</div>
</div>

Children.map è simile a trasformare array con map(). La differenza è che la struttura dati children è considerata opaca. Ciò significa che, anche se a volte è un array, non dovresti assumere che lo sia o che abbia un altro tipo di dato particolare. Per questo motivo, se devi trasformarla, dovresti usare Children.map.

import { Children } from 'react';

export default function RowList({ children }) {
  return (
    <div className="RowList">
      {Children.map(children, child =>
        <div className="Row">
          {child}
        </div>
      )}
    </div>
  );
}

Approfondimento

Perché la prop children non è sempre un array?

In React, la prop children è considerata una struttura dati opaca. Ciò significa che non dovresti fare affidamento su come è strutturata. Per trasformare, filtrare o contare i children, dovresti usare i metodi Children.

In pratica, la struttura dati children è spesso rappresentata internamente come un array. Tuttavia, se c’è un solo child, React non creerà un array aggiuntivo perché ciò porterebbe a un overhead di memoria non necessario. Finché usi i metodi Children invece di ispezionare direttamente la prop children, il tuo codice non si romperà anche se React cambia il modo in cui la struttura dati è effettivamente implementata.

Anche quando children è un array, Children.map ha un comportamento speciale utile. Ad esempio, Children.map combina le key sugli elementi restituiti con le key sui children che gli hai passato. Questo garantisce che i children JSX originali non “perdano” le key anche se vengono avvolti come nell’esempio sopra.

Insidia

La struttura dati children non include l’output renderizzato dei componenti che passi come JSX. Nell’esempio sotto, i children ricevuti da RowList contengono solo due elementi anziché tre:

  1. <p>This is the first item.</p>
  2. <MoreRows />

Per questo motivo in questo esempio vengono generati solo due wrapper di riga:

import RowList from './RowList.js';

export default function App() {
  return (
    <RowList>
      <p>This is the first item.</p>
      <MoreRows />
    </RowList>
  );
}

function MoreRows() {
  return (
    <>
      <p>This is the second item.</p>
      <p>This is the third item.</p>
    </>
  );
}

Non c’è modo di ottenere l’output renderizzato di un componente interno come <MoreRows /> quando manipoli children. Per questo motivo di solito è meglio usare una delle soluzioni alternative.


Eseguire del codice per ogni child

Chiama Children.forEach per iterare su ogni child nella struttura dati children. Non restituisce alcun valore ed è simile al metodo forEach degli array. Puoi usarlo per eseguire logica personalizzata, come costruire il tuo array.

import { Children } from 'react';

export default function SeparatorList({ children }) {
  const result = [];
  Children.forEach(children, (child, index) => {
    result.push(child);
    result.push(<hr key={index} />);
  });
  result.pop(); // Remove the last separator
  return result;
}

Insidia

Come accennato in precedenza, non c’è modo di ottenere l’output renderizzato di un componente interno quando manipoli children. Per questo motivo di solito è meglio usare una delle soluzioni alternative.


Contare i children

Chiama Children.count(children) per calcolare il numero di children.

import { Children } from 'react';

export default function RowList({ children }) {
  return (
    <div className="RowList">
      <h1 className="RowListHeader">
        Total rows: {Children.count(children)}
      </h1>
      {Children.map(children, child =>
        <div className="Row">
          {child}
        </div>
      )}
    </div>
  );
}

Insidia

Come accennato in precedenza, non c’è modo di ottenere l’output renderizzato di un componente interno quando manipoli children. Per questo motivo di solito è meglio usare una delle soluzioni alternative.


Convertire i children in un array

Chiama Children.toArray(children) per trasformare la struttura dati children in un normale array JavaScript. Questo ti permette di manipolare l’array con i metodi array integrati come filter, sort o reverse.

import { Children } from 'react';

export default function ReversedList({ children }) {
  const result = Children.toArray(children);
  result.reverse();
  return result;
}

Insidia

Come accennato in precedenza, non c’è modo di ottenere l’output renderizzato di un componente interno quando manipoli children. Per questo motivo di solito è meglio usare una delle soluzioni alternative.


Alternatives

Nota bene

Questa sezione descrive alternative all’API Children (con la C maiuscola) che si importa così:

import { Children } from 'react';

Non confonderla con l’uso della prop children (con la c minuscola), che è buona pratica ed è incoraggiato.

Esporre più componenti

Manipolare i children con i metodi Children spesso porta a codice fragile. Quando passi children a un componente in JSX, di solito non ti aspetti che il componente manipoli o trasformi i singoli children.

Quando puoi, cerca di evitare l’uso dei metodi Children. Ad esempio, se vuoi che ogni child di RowList sia avvolto in <div className="Row">, esporta un componente Row e avvolgi manualmente ogni riga così:

import { RowList, Row } from './RowList.js';

export default function App() {
  return (
    <RowList>
      <Row>
        <p>This is the first item.</p>
      </Row>
      <Row>
        <p>This is the second item.</p>
      </Row>
      <Row>
        <p>This is the third item.</p>
      </Row>
    </RowList>
  );
}

A differenza dell’uso di Children.map, questo approccio non avvolge automaticamente ogni child. Tuttavia, questo approccio ha un vantaggio significativo rispetto al precedente esempio con Children.map perché funziona anche se continui a estrarre altri componenti. Ad esempio, funziona ancora se estrai il tuo componente MoreRows:

import { RowList, Row } from './RowList.js';

export default function App() {
  return (
    <RowList>
      <Row>
        <p>This is the first item.</p>
      </Row>
      <MoreRows />
    </RowList>
  );
}

function MoreRows() {
  return (
    <>
      <Row>
        <p>This is the second item.</p>
      </Row>
      <Row>
        <p>This is the third item.</p>
      </Row>
    </>
  );
}

Questo non funzionerebbe con Children.map perché “vedrebbe” <MoreRows /> come un singolo child (e una singola riga).


Accettare un array di oggetti come prop

Puoi anche passare esplicitamente un array come prop. Ad esempio, questo RowList accetta un array rows come prop:

import { RowList, Row } from './RowList.js';

export default function App() {
  return (
    <RowList rows={[
      { id: 'first', content: <p>This is the first item.</p> },
      { id: 'second', content: <p>This is the second item.</p> },
      { id: 'third', content: <p>This is the third item.</p> }
    ]} />
  );
}

Poiché rows è un normale array JavaScript, il componente RowList può usare metodi array integrati come map su di esso.

Questo pattern è particolarmente utile quando vuoi poter passare più informazioni come dati strutturati insieme ai children. Nell’esempio sotto, il componente TabSwitcher riceve un array di oggetti come prop tabs:

import TabSwitcher from './TabSwitcher.js';

export default function App() {
  return (
    <TabSwitcher tabs={[
      {
        id: 'first',
        header: 'First',
        content: <p>This is the first item.</p>
      },
      {
        id: 'second',
        header: 'Second',
        content: <p>This is the second item.</p>
      },
      {
        id: 'third',
        header: 'Third',
        content: <p>This is the third item.</p>
      }
    ]} />
  );
}

A differenza del passaggio dei children come JSX, questo approccio ti permette di associare alcuni dati extra come header a ogni elemento. Poiché lavori direttamente con tabs, ed è un array, non hai bisogno dei metodi Children.


Chiamare una render prop per personalizzare la renderizzazione

Invece di produrre JSX per ogni singolo elemento, puoi anche passare una funzione che restituisce JSX e chiamarla quando necessario. In questo esempio, il componente App passa una funzione renderContent al componente TabSwitcher. Il componente TabSwitcher chiama renderContent solo per la tab selezionata:

import TabSwitcher from './TabSwitcher.js';

export default function App() {
  return (
    <TabSwitcher
      tabIds={['first', 'second', 'third']}
      getHeader={tabId => {
        return tabId[0].toUpperCase() + tabId.slice(1);
      }}
      renderContent={tabId => {
        return <p>This is the {tabId} item.</p>;
      }}
    />
  );
}

Una prop come renderContent si chiama render prop perché è una prop che specifica come renderizzare un pezzo dell’interfaccia utente. Tuttavia, non c’è nulla di speciale: è una prop normale che per caso è una funzione.

Le render props sono funzioni, quindi puoi passare loro informazioni. Ad esempio, questo componente RowList passa l’id e l’index di ogni riga alla render prop renderRow, che usa index per evidenziare le righe pari:

import { RowList, Row } from './RowList.js';

export default function App() {
  return (
    <RowList
      rowIds={['first', 'second', 'third']}
      renderRow={(id, index) => {
        return (
          <Row isHighlighted={index % 2 === 0}>
            <p>This is the {id} item.</p>
          </Row>
        );
      }}
    />
  );
}

Questo è un altro esempio di come componenti genitore e figlio possono cooperare senza manipolare i children.


Troubleshooting

Passo un componente personalizzato, ma i metodi Children non mostrano il suo risultato di renderizzazione

Supponiamo di passare due children a RowList così:

<RowList>
<p>First item</p>
<MoreRows />
</RowList>

Se esegui Children.count(children) dentro RowList, otterrai 2. Anche se MoreRows renderizza 10 elementi diversi, o se restituisce null, Children.count(children) sarà comunque 2. Dal punto di vista di RowList, “vede” solo il JSX che ha ricevuto. Non “vede” l’interno del componente MoreRows.

Questa limitazione rende difficile estrarre un componente. Per questo motivo le alternative sono preferite all’uso di Children.