Rendu des composants React à partir d'un tableau d'objets

98

J'ai des données appelées stations qui sont un tableau contenant des objets.

stations : [
  {call:'station one',frequency:'000'},
  {call:'station two',frequency:'001'}
]

Je voudrais rendre un composant d'interface utilisateur pour chaque position de tableau. Jusqu'ici je peux écrire

 var stationsArr = []
 for (var i = 0; i < this.data.stations.length; i++) {
     stationsArr.push(
         <div className="station">
             {this.data}
         </div>
     )
 }

Et puis rendre

render(){
 return (
   {stationsArr}
 )
}

Le problème est que j'imprime toutes les données. Je veux plutôt afficher une clé comme {this.data.call}mais qui n'imprime rien.

Comment puis-je parcourir ces données et renvoyer un nouvel élément d'interface utilisateur pour chaque position du tableau?

thatgibbyguy
la source
Je peux me tromper, mais je pense que vous devez utiliser stationsArrau lieu de l' stationsintérieur de la renderfonction.
Tahir Ahmed

Réponses:

151

Vous pouvez mapper la liste des stations à ReactElements.

Avec React> = 16, il est possible de renvoyer plusieurs éléments du même composant sans avoir besoin d'un wrapper d'élément html supplémentaire. Depuis 16.2, il existe une nouvelle syntaxe <> pour créer des fragments. Si cela ne fonctionne pas ou n'est pas pris en charge par votre IDE, vous pouvez utiliser à la <React.Fragment>place. Entre 16.0 et 16.2, vous pouvez utiliser un polyfill très simple pour les fragments.

Essayez ce qui suit

// Modern syntax >= React 16.2.0
const Test = ({stations}) => (
  <>
    {stations.map(station => (
      <div className="station" key={station.call}>{station.call}</div>
    ))}
  </>
); 

// Modern syntax < React 16.2.0
// You need to wrap in an extra element like div here

const Test = ({stations}) => (
  <div>
    {stations.map(station => (
      <div className="station" key={station.call}>{station.call}</div>
    ))}
  </div>
); 

// old syntax
var Test = React.createClass({
    render: function() {
        var stationComponents = this.props.stations.map(function(station) {
            return <div className="station" key={station.call}>{station.call}</div>;
        });
        return <div>{stationComponents}</div>;
    }
});

var stations = [
  {call:'station one',frequency:'000'},
  {call:'station two',frequency:'001'}
]; 

ReactDOM.render(
  <div>
    <Test stations={stations} />
  </div>,
  document.getElementById('container')
);

N'oubliez pas l' keyattribut!

https://jsfiddle.net/69z2wepo/14377/

Sébastien Lorber
la source
@thatgibbyguy: Oh oui! cela pourrait être la bonne réponse. Il doit y avoir une enveloppe autour de vos composants enfants. Votre renderfonction doit renvoyer un seul élément.
Tahir Ahmed
Quelle serait la raison de suggérer un attribut clé pour chaque élément de station? Ce que je demande, c'est, qu'est-ce que cela changerait si ce n'est pas nécessaire maintenant?
thatgibbyguy
4
@thatgibbyguy dans ce cas, cela n'apporte pas beaucoup d'avantage. Dans des exemples plus avancés, cela permet d'avoir de meilleures performances de rendu car React peut facilement savoir si un nœud existant a été déplacé vers un autre endroit dans le tableau de la station, évitant ainsi de détruire et de recréer un nœud dom existant (et également de garder le nœud dom monté) . C'est dans le doc de réaction: facebook.github.io/react/docs/reconciliation.html#keys
Sebastien Lorber
Légèrement hors sujet, mais je ne sais pas comment créer une requête pour poser cette question. Lorsque vous utilisez la syntaxe ES6 dans votre exemple ci-dessus, comment procéder pour transmettre l'index de la carte? IOW, comment puis-je savoir si je suis dans le dernier nœud du tableau? J'ai essayé d'envelopper des parens et cela ne semblait pas bien se passer.
Lane Goolsby le
@ElHombre stations.map((station,index) => { })fonctionne bien pour moi
Sebastien Lorber
46

J'ai une réponse qui pourrait être un peu moins déroutante pour les débutants comme moi. Vous pouvez simplement utiliser mapdans la méthode de rendu des composants.

render () {
   return (
       <div>
           {stations.map(station => <div key={station}> {station} </div>)} 
       </div>
   );
}
Thomas Valadez
la source
11
Cela nécessite un keyaccessoire reactjs.org/docs/lists-and-keys.html#keys
David Barratt
1
Parfois, cela est beaucoup plus utile. :)
Ferit
6

this.data contient probablement toutes les données, vous devrez donc faire quelque chose comme ceci:

var stations = [];
var stationData = this.data.stations;

for (var i = 0; i < stationData.length; i++) {
    stations.push(
        <div key={stationData[i].call} className="station">
            Call: {stationData[i].call}, Freq: {stationData[i].frequency}
        </div>
    )
}

render() {
  return (
    <div className="stations">{stations}</div>
  )
}

Ou vous pouvez utiliser les mapfonctions fléchées et si vous utilisez ES6:

const stations = this.data.stations.map(station =>
    <div key={station.call} className="station">
      Call: {station.call}, Freq: {station.frequency}
    </div>
);
Dominique
la source
2
Cela ne fonctionnera pas dans la version actuelle de React, vous ne pouvez pas renvoyer un tableau.
Aftab Naveed
@AftabNaveed merci de l'avoir mis à jour, le rendu devrait renvoyer un élément mais il est valide d'avoir un tableau d'éléments à l'intérieur
Dominic
Comme le dit @AftabNaveed, si react version <16, vous devrez utiliser ci-dessus, sinon vous pouvez simplement return stations;( codepen.io/pawelgrzybek/pen/WZEKWj )
Chicken Soup
1

Il y a quelques moyens qui peuvent être utilisés.

const stations = [
  {call:'station one',frequency:'000'},
  {call:'station two',frequency:'001'}
];
const callList = stations.map(({call}) => call)

Solution 1

<p>{callList.join(', ')}</p>

Solution 2

<ol>    
  { callList && callList.map(item => <li>{item}</li>) }
</ol>

Modifier kind-antonelli-z8372

Bien sûr, il existe d'autres moyens également disponibles.

Mo.
la source