Renderizando Componentes React a partir de Matriz de Objetos

98

Eu tenho alguns dados chamados de estações que são uma matriz que contém objetos.

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

Eu gostaria de renderizar um componente de interface do usuário para cada posição da matriz. Até agora eu posso escrever

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

E então renderizar

render(){
 return (
   {stationsArr}
 )
}

O problema é que estou imprimindo todos os dados. Em vez disso, quero apenas mostrar uma chave como, {this.data.call}mas não imprime nada.

Como posso fazer um loop por esses dados e retornar um novo elemento de interface do usuário para cada posição da matriz?

aquele gibbyguy
fonte
Posso estar errado, mas acho que você precisa usar em stationsArrvez de stationsdentro da renderfunção.
Tahir Ahmed

Respostas:

151

Você pode mapear a lista de estações para ReactElements.

Com React> = 16, é possível retornar vários elementos do mesmo componente sem a necessidade de um wrapper de elemento html extra. Desde 16.2, existe uma nova sintaxe <> para criar fragmentos. Se isso não funcionar ou não for suportado por seu IDE, você pode usar <React.Fragment>. Entre 16.0 e 16.2, você pode usar um polyfill muito simples para fragmentos.

Tente o seguinte

// 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ão se esqueça do keyatributo!

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

Sebastien Lorber
fonte
@thatgibbyguy: Oh sim! essa pode ser a resposta certa. É necessário que haja um envolvimento em torno de seus componentes filhos. Sua renderfunção deve retornar um único elemento.
Tahir Ahmed
Qual seria a razão para sugerir um atributo-chave para cada elemento da estação? O que estou perguntando é: o que mudaria se não fosse necessário agora?
thatgibbyguy
4
@thatgibbyguy nesse caso não traz muita vantagem. Em exemplos mais avançados, permite ter um melhor desempenho de renderização, pois o React pode facilmente saber se um nó existente foi movido para outro lugar na matriz da estação, evitando assim destruir e recriar um nó dom existente (e também manter o nó dom montado) . Está no documento de reação: facebook.github.io/react/docs/reconciliation.html#keys
Sebastien Lorber
Um pouco fora do tópico, mas não tenho certeza de como construir uma consulta para fazer isso. Ao usar a sintaxe ES6 em seu exemplo acima, como alguém faria para passar o índice do mapa? IOW, como posso saber se estou no último nó da matriz? Tentei embrulhar em parênteses e não pareceu correr bem.
Lane Goolsby
@ElHombre stations.map((station,index) => { })funciona bem para mim
Sebastien Lorber
46

Tenho uma resposta que pode ser um pouco menos confusa para iniciantes como eu. Você pode apenas usar mapdentro do método de renderização de componentes.

render () {
   return (
       <div>
           {stations.map(station => <div key={station}> {station} </div>)} 
       </div>
   );
}
Thomas Valadez
fonte
11
Isso precisa de um keyprop reactjs.org/docs/lists-and-keys.html#keys
David Barratt,
1
Às vezes, isso é muito mais útil. :)
Ferit
6

this.data presumivelmente contém todos os dados, então você precisaria fazer algo assim:

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 você pode usar mapas funções de seta e se estiver usando ES6:

const stations = this.data.stations.map(station =>
    <div key={station.call} className="station">
      Call: {station.call}, Freq: {station.frequency}
    </div>
);
Dominic
fonte
2
Isso não funcionará na versão React atual, você não pode retornar um array.
Aftab Naveed
@AftabNaveed obrigado, eu atualizei, o render deve retornar um elemento, mas é válido ter um array de elementos dentro dele
Dominic
Como @AftabNaveed diz, se a versão de reação <16, você precisará usar acima, caso contrário, você pode apenas return stations;( codepen.io/pawelgrzybek/pen/WZEKWj )
Canja de Galinha
1

Existem algumas maneiras que podem ser usadas.

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

Solução 1

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

Solução 2

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

Editar kind-antonelli-z8372

Claro que também existem outras formas disponíveis.

Mo.
fonte