Comment renvoyer plusieurs lignes JSX dans une autre instruction return dans React?

100

Une seule ligne fonctionne bien

render: function () {
  return (
    {[1,2,3].map(function (n) {
      return <p>{n}</p>
    }}
  );
}

pas pour plusieurs lignes

render: function () {
  return (
    {[1,2,3].map(function (n) {
      return (
        <h3>Item {n}</h3>
        <p>Description {n}</p>
      )
    }}
  );
}

Merci.

Shawn
la source
1
Pour plus d'informations sur ce problème particulier: github.com/facebook/react/issues/2127
plus-
pas return ("asdf" "asdf");vous voulezreturn ["asdf", "asdf"];
neaumusic

Réponses:

149

Essayez de considérer les balises comme des appels de fonction (voir la documentation ). Alors le premier devient:

{[1,2,3].map(function (n) {
  return React.DOM.p(...);
})}

Et le second:

{[1,2,3].map(function (n) {
  return (
    React.DOM.h3(...)
    React.DOM.p(...)
  )
})}

Il devrait maintenant être clair que le deuxième extrait de code n'a pas vraiment de sens (vous ne pouvez pas renvoyer plus d'une valeur dans JS). Vous devez soit l'envelopper dans un autre élément (très probablement ce que vous voudriez, de cette façon vous pouvez également fournir une keypropriété valide ), ou vous pouvez utiliser quelque chose comme ceci:

{[1,2,3].map(function (n) {
  return ([
    React.DOM.h3(...),
    React.DOM.p(...)
  ]);
})}

Avec du sucre JSX:

{[1,2,3].map(function (n) {
  return ([
    <h3></h3>, // note the comma
    <p></p>
  ]);
})}

Vous n'avez pas besoin d'aplatir le tableau résultant, React le fera pour vous. Voir le violon suivant http://jsfiddle.net/mEB2V/1/ . Encore une fois: envelopper les deux éléments dans une div / section sera probablement mieux à long terme.

Jan Olaf Krems
la source
4
Oui, en fait, clairement documenté facebook.github.io/react/tips/…
Shawn
1
Utiliser return ([...]) sans le bit flatten me donne le balisage exactement comme je le voulais, bien que le tableau retourné ne doive pas avoir été flattend mais dans mon cas particulier, cela n'affecte pas la sortie finale. Ou est-ce?
Shawn
Merci! TIL! Mettre à jour ma réponse pour inclure un lien vers un JSFiddle qui montre que l'aplatissement est facultatif. Inclura également le lien vers les documents React.
Jan Olaf Krems
13
Cela ne fonctionne plus (à partir de 0.9ish)Uncaught Error: Invariant Violation: Product.render(): A valid ReactComponent must be returned. You may have returned undefined, an array or some other invalid object.
dogmatic69
2
@TimFletcher C'est bien de renvoyer un tableau dans le cadre du rendu d'un composant, par exemple <div>{ this.props.foos.map(function() { return <Foo /> }) }</div>. Mais la renderfonction du composant ne peut pas renvoyer ce tableau sans l'envelopper, par exemple dans un div.
Henrik N
35

Il semble que l'ancienne réponse concernant le retour d'un tableau ne s'applique plus (peut-être depuis React ~ 0.9, comme @ dogmatic69 l'a écrit dans un commentaire ).

La documentation indique que vous devez renvoyer un seul nœud:

Nombre maximum de nœuds racine JSX

Actuellement, dans le rendu d'un composant, vous ne pouvez renvoyer qu'un seul nœud; si vous avez, par exemple, une liste de div à renvoyer, vous devez envelopper vos composants dans un div, un span ou tout autre composant.

N'oubliez pas que JSX se compile en JS normal; renvoyer deux fonctions n'a pas vraiment de sens syntaxique. De même, ne mettez pas plus d'un enfant dans un ternaire.

Dans de nombreux cas, vous pouvez simplement envelopper les choses dans un <div>fichier <span>.

Dans mon cas, je voulais retourner plusieurs <tr>s. Je les ai enveloppés dans un <tbody>- une table peut avoir plusieurs corps.

EDIT: À partir de React 16.0, le retour d'un tableau est apparemment à nouveau autorisé, tant que chaque élément a un key: https://facebook.github.io/react/blog/2017/09/26/react-v16.0.html # new-render-return-types-fragments-and-strings

EDIT: React 16.2 vous permet d'entourer une liste d'éléments avec <Fragment>…</Fragment>ou même <>…</>, si vous préférez cela à un tableau: https://blog.jmes.tech/react-fragment-and-semantic-html/

Henrik N
la source
3
Que pouvez-vous faire si vous souhaitez retourner plusieurs <li>? En supposant que je ne peux pas tout emballer<ul>
Banjocat
@Banjocat J'ai peur de ne pas savoir: / Vous êtes autorisé à imbriquer des listes, vous pouvez donc renvoyer quelque chose comme <li><ul><li>one</li><li>two</li></ul></li>si cela fonctionne dans votre situation. Ou: Un div wrapping ne serait pas strictement valide, mais peut-être qu'il fonctionne correctement dans tous les navigateurs concernés? Si vous essayez, faites-le nous savoir.
Henrik N
1
@Banjocat ... J'aimerais avoir une meilleure réponse à votre question. Vous devriez peut-être la poser comme une question de stackoverflow régulière et voir si vous obtenez une réponse différente.
user1175849
@ user1175849 Vous pourriez peut-être poster cette question alors :)
Henrik N
1
@HenrikN FWIW, enroulant un « sous - ensemble » de liun spanou divn'a pas bien fonctionné pour moi. Le div a sérieusement cassé le rendu et, au moins dans mon cas d'utilisation, la durée a gâché le CSS. 2 ¢: Essayer de renvoyer plusieurs sous-ensembles de lis est une odeur de code. Nous utilisions a ulcomme une sorte de menu déroulant, et je voulais au départ que de nombreux composants renvoient des "sections" de lis. En fin de compte, il valait mieux mettre tout le code de menu dans un seul composant «ancré» à ulplutôt que chausse-pied en lis provenant de sources multiples. Je pense que cela a également rendu le modèle mental de l'interface utilisateur un peu plus propre.
ruffin
13

À partir de React v16.0.0 , il est possible de renvoyer plusieurs éléments en les enveloppant dans unArray

render() {
  return (
    {[1,2,3].map(function (n) {
      return [
        <h3>Item {n}</h3>.
        <p>Description {n}</p>
      ]
    }}
  );
}

Également à partir de React v16.2.0 , une nouvelle fonctionnalité appelée React Fragmentsest introduite que vous pouvez utiliser pour envelopper plusieurs éléments

render() {
  return (
    {[1,2,3].map(function (n, index) {
      return (
        <React.Fragment key={index}>
            <h3>Item {n}</h3>
            <p>Description {n}</p>
        </React.Fragment>
      )
    }}
  );
}

Selon la documentation:

Un modèle courant dans React consiste pour un composant à renvoyer plusieurs éléments. Les fragments vous permettent de regrouper une liste d'enfants sans ajouter de nœuds supplémentaires au DOM.

Les fragments déclarés avec la syntaxe explicite peuvent avoir des clés. Un cas d'utilisation pour cela est le mappage d'une collection à un tableau de fragments - par exemple, pour créer une liste de description:

function Glossary(props) {
  return (
    <dl>
      {props.items.map(item => (
        // Without the `key`, React will fire a key warning
        <React.Fragment key={item.id}>
          <dt>{item.term}</dt>
          <dd>{item.description}</dd>
        </React.Fragment>
      ))}
    </dl>
  );
}

key est le seul attribut qui peut être passé à Fragment. À l'avenir, nous pourrions ajouter la prise en charge d'attributs supplémentaires, tels que les gestionnaires d'événements.

Shubham Khatri
la source
7

En outre, vous souhaiterez peut-être renvoyer plusieurs éléments de liste dans une fonction d'assistance à l'intérieur d'un composant React. Renvoyez simplement un tableau de nœuds html avec l' keyattribut:

import React, { Component } from 'react'

class YourComponent extends Component {
  // ...

  render() {
    return (
      <ul>
        {this.renderListItems()}
      </ul>
    )
  }      

  renderListItems() {
    return [
      <li key={1}><a href="#">Link1</a></li>,
      <li key={2}><a href="#">Link2</a></li>,
      <li key={3} className="active">Active item</li>,
    ]
  }
}
Dmitriy
la source
2

Vous pouvez utiliser createFragmentici.

https://facebook.github.io/react/docs/create-fragment.html

import createFragment from 'react-addons-create-fragment';
...
{[1,2,3].map((n) => createFragment({
    h: <h3>...</h3>,
    p: <p>...</p>
  })
)}

(en utilisant la syntaxe ES6 et JSX ici)

vous devez d'abord ajouter le react-addons-create-fragmentpackage:

npm install --save react-addons-create-fragment

Avantage par rapport à la solution de Jan Olaf Krems: React ne se plaint pas des disparus key

Sylvain
la source
corrigez-moi si je me trompe, mais vous pouvez simplement ajouter les clés manuellement. en utilisant l'exemple de jan: le premier élément du tableau obtient par exemple <h3 key = {i}> </h3> et le deuxième élément du tableau sth légèrement différent comme <p> key = {i + '-foo'}> </p>
nerdess
2

Actualisé

Utilisez React Fragment. C'est simple. Lien vers la documentation des fragments.

render() {
  return (
    <>
    {[1,2,3].map((value) => <div>{value}</div>)}
    </>
  );
}

Ancienne réponse - obsolète

Avec React> 16, vous pouvez utiliser react-composite .

import { Composite } from 'react-composite';

// ...

{[1,2,3].map((n) => (
  <Composite>
    <h2>Title {n}</h2>
    <p>Description {n}</p>
  </Composite>
))};

Bien sûr, react-composite doit être installé.

npm install react-composite --save
Monsieur Br
la source
0

C'est simple par fragment React <></>et React.Fragment:

return (
    <>
      {[1, 2, 3].map(
        (n, index): ReactElement => (
          <React.Fragment key={index}>
            <h3>Item {n}</h3>
            <p>Description {n}</p>
          </React.Fragment>
        ),
      )}
    </>
  );
Masih Jahangiri
la source