Отрисовка компонентов React из массива объектов


100

У меня есть данные, называемые станциями, которые представляют собой массив, содержащий объекты.

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

Я хотел бы отобразить компонент пользовательского интерфейса для каждой позиции массива. Пока я могу написать

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

А затем визуализировать

render(){
 return (
   {stationsArr}
 )
}

Проблема в том, что я распечатываю все данные. Вместо этого я хочу просто показать такой ключ, {this.data.call}но он ничего не печатает.

Как я могу просмотреть эти данные и вернуть новый элемент пользовательского интерфейса для каждой позиции массива?


Я могу ошибаться, но я думаю, что вам нужно использовать stationsArrвместо функции stationsвнутри render.
Тахир Ахмед

Ответы:


151

Вы можете сопоставить список станций с ReactElements.

С React> = 16 можно возвращать несколько элементов из одного и того же компонента без необходимости в дополнительной оболочке элемента html. Начиная с версии 16.2, появился новый синтаксис <> для создания фрагментов. Если это не работает или не поддерживается вашей IDE, вы можете использовать <React.Fragment>вместо него. Между 16.0 и 16.2 вы можете использовать очень простой полифилл для фрагментов.

Попробуйте следующее

// 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')
);

Не забудьте keyатрибут!

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


@thatgibbyguy: О да! это может быть правильный ответ. Должна быть оболочка вокруг ваших дочерних компонентов. Ваша renderфункция должна возвращать один единственный элемент.
Тахир Ахмед

В чем причина предложения ключевого атрибута для каждого элемента станции? Я спрашиваю, что бы это изменилось, если бы оно сейчас не нужно?
thatgibbyguy

4
@thatgibbyguy в данном случае особой пользы не приносит. В более сложных примерах это позволяет улучшить производительность рендеринга, поскольку React может легко узнать, был ли существующий узел перемещен в другое место в массиве станций, что позволяет избежать уничтожения и воссоздания существующего узла dom (а также сохранить узел dom смонтированным) . Это в ответном документе: facebook.github.io/react/docs/reconciliation.html#keys
Себастьен Лорбер,

Немного не по теме, но я не уверен, как составить запрос, чтобы задать этот вопрос. При использовании синтаксиса ES6 в приведенном выше примере, как можно передать индекс с карты? IOW, как я могу определить, нахожусь ли я в последнем узле массива? Я пробовала обернуть его пареной, но это не помогло.
Lane Goolsby,

@ElHombre stations.map((station,index) => { })отлично работает для меня,
Себастьен Лорбер,

46

У меня есть ответ, который может быть немного менее запутанным для таких новичков, как я. Вы можете просто использовать mapвнутри метода рендеринга компонентов.

render () {
   return (
       <div>
           {stations.map(station => <div key={station}> {station} </div>)} 
       </div>
   );
}

11
Для этого нужна keyопора responsejs.org/docs/lists-and-keys.html#keys
Дэвид Барратт,

1
Иногда это бывает намного полезнее. :)
Ferit

6

this.data предположительно содержит все данные, поэтому вам нужно будет сделать что-то вроде этого:

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>
  )
}

Или вы можете использовать mapи стрелочные функции, если вы используете ES6:

const stations = this.data.stations.map(station =>
    <div key={station.call} className="station">
      Call: {station.call}, Freq: {station.frequency}
    </div>
);

2
Это не будет работать в текущей версии React, вы не можете вернуть массив.
Афтаб Навид

@AftabNaveed, спасибо, я обновил его, рендер должен возвращать один элемент, но допустимо иметь внутри него массив элементов
Доминик

Как говорит @AftabNaveed, если реакция версии <16, вам нужно будет использовать выше, в противном случае вы можете просто return stations;( codepen.io/pawelgrzybek/pen/WZEKWj )
Куриный суп

1

Есть несколько способов, которые можно использовать.

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

Решение 1

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

Решение 2

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

Редактировать kind-antonelli-z8372

Конечно, есть и другие способы.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.