Children
vous permet de manipuler et transformer les contenus JSX que votre composant reçoit via la prop children
.
const mappedChildren = Children.map(children, child =>
<div className="Row">
{child}
</div>
);
Référence
Children.count(children)
Appelez Children.count(children)
pour compter les enfants dans la structure de données children
.
import { Children } from 'react';
function RowList({ children }) {
return (
<>
<h1>Nombre de lignes : {Children.count(children)}</h1>
...
</>
);
}
Voir d’autres exemples ci-dessous.
Paramètres
children
: la valeur de la propchildren
reçue par votre composant.
Valeur renvoyée
Le nombre de nœuds dans ces children
.
Limitations
- Les nœuds vides (
null
,undefined
et les booléens), les chaînes de caractères, les nombres, les éléments React sont tous comptabilisés. Les tableaux ne comptent pas comme des nœuds individuels, mais leurs enfants si. La traversée s’arrête aux éléments React : leur rendu n’est pas effectué, et leurs enfants ne sont pas traversés. Les Fragments ne sont pas traversés non plus.
Children.forEach(children, fn, thisArg?)
Appelez Children.forEach(children, fn, thisArg?)
pour exécuter du code pour chaque enfant dans la structure de données children
.
import { Children } from 'react';
function SeparatorList({ children }) {
const result = [];
Children.forEach(children, (child, index) => {
result.push(child);
result.push(<hr key={index} />);
});
// ...
Voir d’autres exemples ci-dessous.
Paramètres
children
: la valeur de la propchildren
reçue par votre composant.fn
: la fonction que vous souhaitez exécuter pour chaque enfant, comme la fonction de rappel de la méthodeforEach
des tableaux. Elle sera appelée avec l’enfant comme premier argument et son index en second argument. L’index démarre à0
et s’incrémente à chaque appel.thisArg
optionnel : la valeur dethis
avec laquella la fonctionfn
sera appelée. S’il est manquant,this
seraundefined
.
Valeur renvoyée
Children.forEach
renvoie undefined
.
Limitations
- Les nœuds vides (
null
,undefined
et les booléens), les chaînes de caractères, les nombres, les éléments React sont tous comptabilisés. Les tableaux ne comptent pas comme des nœuds individuels, mais leurs enfants si. La traversée s’arrête aux éléments React : leur rendu n’est pas effectué, et leurs enfants ne sont pas traversés. Les Fragments ne sont pas traversés non plus.
Children.map(children, fn, thisArg?)
Appelez Children.map(children, fn, thisArg?)
pour produire une transformée de chaque enfant dans la structure de données children
.
import { Children } from 'react';
function RowList({ children }) {
return (
<div className="RowList">
{Children.map(children, child =>
<div className="Row">
{child}
</div>
)}
</div>
);
}
Voir d’autres exemples ci-dessous.
Paramètres
children
: la valeur de la propchildren
reçue par votre composant.fn
: la fonction que vous souhaitez exécuter pour chaque enfant, comme la fonction de rappel de la méthodeforEach
des tableaux. Elle sera appelée avec l’enfant comme premier argument et son index en second argument. L’index démarre à0
et s’incrémente à chaque appel. Cette fonction doit renvoyer un nœud React. Ça peut être un nœud vide (null
,undefined
ou un booléen), une chaîne de caractères, un nombre, un élément React ou un tableau de nœuds React.thisArg
optionnel : la valeur dethis
avec laquella la fonctionfn
sera appelée. S’il est manquant,this
seraundefined
.
Valeur renvoyée
Si children
est null
ou undefined
, renvoie la même valeur.
Dans le cas contraire, renvoie un tableau plat constitué des nœuds que vous avez renvoyé depuis la fonction fn
. Le tableau renvoyé contiendra tous les nœuds à l’exception de null
et undefined
.
Limitations
-
Les nœuds vides (
null
,undefined
et les booléens), les chaînes de caractères, les nombres, les éléments React sont tous comptabilisés. Les tableaux ne comptent pas comme des nœuds individuels, mais leurs enfants si. La traversée s’arrête aux éléments React : leur rendu n’est pas effectué, et leurs enfants ne sont pas traversés. Les Fragments ne sont pas traversés non plus. -
Si vous renvoyez un élément ou un tableau d’éléments avec des clés depuis
fn
, les clés des éléments renvoyés seront automatiquement combinées avec la clé de l’élément correspondant danschildren
. Lorsque vous renvoyez plusieurs éléments depuisfn
sous forme d’un tableau, leurs clés n’ont besoin d’être uniques qu’entre elles.
Children.only(children)
Appelez Children.only(children)
pour garantir que children
représente un seul élément React.
function Box({ children }) {
const element = Children.only(children);
// ...
Paramètres
children
: la valeur de la propchildren
reçue par votre composant.
Valeur renvoyée
Si children
est un élément valide, renvoie cet élément.
Dans le cas contraire, lève une erreur.
Limitations
- Cette méthode lève une erreur si vous passez un tableau (tel que le résultat d’un appel à
Children.map
) commechildren
. En d’autres termes, elle s’assure quechildren
représente un seul élément React, et non un tableau contenant un seul élément React.
Children.toArray(children)
Appelez Children.toArray(children)
pour créer un tableau à partir de la structure de données children
.
import { Children } from 'react';
export default function ReversedList({ children }) {
const result = Children.toArray(children);
result.reverse();
// ...
Paramètres
children
: la valeur de la propchildren
reçue par votre composant.
Valeur renvoyée
Renvoie un tableau plat des éléments de children
.
Limitations
- Les nœuds vides (
null
,undefined
et les booléens) seront omis du tableau renvoyé. Les clés des éléments renvoyés seront calculées à partir des clés des éléments d’origine, de leur profondeur et de leur position. Ça garantit que l’aplatissement du tableau n’altèrera pas le comportement.
Utilisation
Transformer les nœuds enfants
Pour transformer les enfants JSX que votre composant reçoit dans sa prop children
, appelez Children.map
:
import { Children } from 'react';
function RowList({ children }) {
return (
<div className="RowList">
{Children.map(children, child =>
<div className="Row">
{child}
</div>
)}
</div>
);
}
Dans l’exemple ci-dessus, la RowList
enrobe chaque enfant qu’elle reçoit dans un conteneur <div className="Row">
. Disons par exemple que le composant parent passe trois balises <p>
dans la prop children
de RowList
:
<RowList>
<p>Voici le premier élément.</p>
<p>Voici le deuxième.</p>
<p>Et voici le troisième.</p>
</RowList>
Dans ce cas, avec l’implémentation de RowList
ci-dessus, le rendu final ressemblera à ceci :
<div className="RowList">
<div className="Row">
<p>Voici le premier élément.</p>
</div>
<div className="Row">
<p>Voici le deuxième.</p>
</div>
<div className="Row">
<p>Et voici le troisième.</p>
</div>
</div>
Children.map
ressemble à une transformation de tableaux avec map()
. La différence vient de ce que la structure de données children
est considérée opaque. Ça signifie que même s’il s’agit parfois d’un tableau, vous ne devriez pas supposer qu’elle en soit effectivement un, ou de n’importe quel autre type particulier d’ailleurs. C’est pourquoi vous devriez utiliser Children.map
si vous avez besoin de la transformer.
import { Children } from 'react'; export default function RowList({ children }) { return ( <div className="RowList"> {Children.map(children, child => <div className="Row"> {child} </div> )} </div> ); }
En détail
Dans React, la prop children
est vue comme une structure de données opaque. Ça signifie que vous ne devriez pas dépendre de sa structure effective. Pour transformer, filtrer ou compter les nœuds enfants, vous devriez utiliser les méthodes de Children
.
En pratique, la structure de données children
est souvent représentée en interne par un tableau. Ceci dit, s’il n’y a qu’un seul enfant, React ne créera pas de tableau superflu afin de ménager la mémoire. Tant que vous utilisez les méthodes de Children
au lieu de manipuler ou inspecter directement la prop children
, votre code ne cassera pas, même si React modifie l’implémentation effective de la structure de données.
Même lorsque children
est bien un tableau, Children.map
a des particularités utiles. Par exemple, Children.map
combine les clés des éléments renvoyés avec celles des children
que vous lui aviez passés. Ça permet de garantir que les enfants JSX d’origine ne « perdent » pas leurs clés même s’ils sont enrobés comme dans l’exemple ci-dessus.
Exécuter du code pour chaque enfant
Appelez Children.forEach
pour itérer sur chaque enfant dans la structure de données children
. Elle ne renvoie aucune valeur et fonctionne de façon similaire à la méthode forEach
des tableaux. Vous pouvez l’utiliser pour exécuter de la logique personnalisée, comme la construction de votre propre tableau.
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(); // Retrait du dernier séparateur return result; }
Compter les nœuds enfants
Appelez Children.count(children)
pour calculer le nombre de nœuds enfants.
import { Children } from 'react'; export default function RowList({ children }) { return ( <div className="RowList"> <h1 className="RowListHeader"> Nombre de lignes : {Children.count(children)} </h1> {Children.map(children, child => <div className="Row"> {child} </div> )} </div> ); }
Convertir les enfants en tableau
Appelez Children.toArray(children)
pour transformer la structure de données children
en un véritable tableau JavaScript. Ça vous permet de le manipuler avec les méthodes natives des tableaux telles que filter
, sort
ou reverse
.
import { Children } from 'react'; export default function ReversedList({ children }) { const result = Children.toArray(children); result.reverse(); return result; }
Alternatives
Exposer plusieurs composants
La manipulation des nœuds enfants avec les méthodes de Children
fragilise souvent le code. Lorsque vous passez des enfants à un composant en JSX, vous ne vous attendez généralement pas à ce que le composant manipule ou transforme individuellement ces enfants.
Autant que possible, évitez les méthodes de Children
. Si par exemple vous souhaitez que chaque enfant d’une RowList
soit enrobé par un <div className="Row">
, exportez un composant Row
et enrobez chaque ligne avec manuellement, comme ceci :
import { RowList, Row } from './RowList.js'; export default function App() { return ( <RowList> <Row> <p>Voici le premier élément.</p> </Row> <Row> <p>Voici le deuxième.</p> </Row> <Row> <p>Et voici le troisième.</p> </Row> </RowList> ); }
Contrairement au recours à Children.map
, cette approche n’enrobe pas automatiquement chaque enfant. Ceci dit, cette approche a un avantage considérable par rapport à l’exemple précédent avec Children.map
, parce qu’elle marche même si vous continuez à extraire davantage de composants. Par exemple, elle fonctionnera toujours si vous extrayez votre propre composant MoreRows
:
import { RowList, Row } from './RowList.js'; export default function App() { return ( <RowList> <Row> <p>Voici le premier élément.</p> </Row> <MoreRows /> </RowList> ); } function MoreRows() { return ( <> <Row> <p>Voici le deuxième.</p> </Row> <Row> <p>Et voici le troisième.</p> </Row> </> ); }
Ça ne fonctionnerait pas avec Children.map
parce qu’elle « verrait » <MoreRows />
comme un enfant unique (et donc une ligne unique).
Accepter un tableau d’objets comme prop
Vous pouvez aussi passer explicitement un tableau comme prop. La RowList
ci-dessous accepte par exemple un tableau pour sa prop rows
:
import { RowList, Row } from './RowList.js'; export default function App() { return ( <RowList rows={[ { id: 'first', content: <p>Voici le premier élément.</p> }, { id: 'second', content: <p>Voici le deuxième.</p> }, { id: 'third', content: <p>Et voici le troisième.</p> } ]} /> ); }
Dans la mesure où rows
est un tableau JavaScript standard, le composant RowList
peut utiliser ses méthodes de tableau natives, telles que map
.
Cette approche est particulièrement utile lorsque vous souhaitez pouvoir passer davantage d’informations structurées en complément des enfants. Dans l’exemple qui suit, le composant TabSwitcher
reçoit un tableau d’objets dans sa prop tabs
:
import TabSwitcher from './TabSwitcher.js'; export default function App() { return ( <TabSwitcher tabs={[ { id: 'first', header: 'Premier', content: <p>Voici le premier élément.</p> }, { id: 'second', header: 'Deuxième', content: <p>Voici le deuxième.</p> }, { id: 'third', header: 'Troisième', content: <p>Et voici le troisième.</p> } ]} /> ); }
Contrairement au passage des enfants par JSX, cette approche vous permet d’associer des données supplémentaires à chaque élément, comme par exemple header
. Puisque vous travaillez directement avec tabs
et qu’il s’agit d’un tableau, vous n’avez pas besoin des méthodes de Children
.
Appeler une prop de rendu pour personnaliser le rendu
Plutôt que de produire du JSX pour chaque élément individuel, vous pouvez passer une fonction qui renverra ce JSX, puis appeler cette fonction lorsque nécessaire. Dans l’exemple ci-après, le composant App
passe une fonction renderContent
au composant TabSwitcher
. Le composant TabSwitcher
appelle renderContent
uniquement pour l’onglet sélectionné :
import TabSwitcher from './TabSwitcher.js'; export default function App() { return ( <TabSwitcher tabIds={['premier', 'deuxième', 'troisième']} getHeader={tabId => { return tabId[0].toUpperCase() + tabId.slice(1); }} renderContent={tabId => { return <p>Voici le {tabId} élément.</p>; }} /> ); }
Une prop comme renderContent
est appelée une prop de rendu parce qu’elle décrit comment faire le rendu d’un bout d’interface utilisateur. Ceci étant dit, elle n’a rien de spécial : c’est une prop comme une autre, dont la valeur se trouve être une fonction.
Les props de rendu sont des fonctions, vous pouvez donc leur passer des informations. Le composant RowList
ci-dessous passe par exemple l’id
et l’index
de chaque ligne à la prop de rendu renderRow
, qui utilise index
pour mettre en exergue les lignes paires :
import { RowList, Row } from './RowList.js'; export default function App() { return ( <RowList rowIds={['premier', 'deuxième', 'troisième']} renderRow={(id, index) => { return ( <Row isHighlighted={index % 2 === 0}> <p>Voici le {id} élément</p> </Row> ); }} /> ); }
C’est un autre exemple de coopération entre composants parents et enfants sans manipulation des enfants.
Dépannage
Je passe un composant personnalisé, mais les méthodes de Children
n’affichent pas son rendu
Supposons que vous passiez deux enfants à RowList
comme ceci :
<RowList>
<p>Premier élément</p>
<MoreRows />
</RowList>
Si vous faites un Children.count(children)
au sein de RowList
, vous obtiendrez 2
. Même si MoreRows
produit 10 éléments différents, ou s’il renvoie null
, Children.count(children)
vaudra tout de même 2
. Du point de vue de RowList
, il ne « voit » que le JSX qu’il reçoit. Il ne « voit » pas la tambouille interne du composant MoreRows
.
Cette limitation complexifie l’extraction d’une partie du contenu dans un composant dédié. C’est pourquoi vous devriez préférer les alternatives à l’utilisation de Children
.