Я пытаюсь сделать что-то вроде следующего в React JSX (где ObjectRow является отдельным компонентом):

<tbody>
    for (var i=0; i < numrows; i++) {
        <ObjectRow/>
    } 
</tbody>

Я понимаю и понимаю, почему это недопустимо JSX, поскольку JSX отображается на вызовы функций. Однако, исходя из шаблона земли и будучи новичком в JSX, я не уверен, как бы я достиг вышеуказанного (добавление компонента несколько раз).

1627
B Robster 5 Апр 2014 в 09:29

28 ответов

Лучший ответ

Думайте об этом, как будто вы просто вызываете функции JavaScript. Вы не можете использовать цикл for, куда будут идти аргументы для вызова функции:

return tbody(
    for (var i = 0; i < numrows; i++) {
        ObjectRow()
    } 
)

Посмотрите, как функции tbody передается цикл for в качестве аргумента, и, конечно, это синтаксическая ошибка.

Но вы можете создать массив, а затем передать его в качестве аргумента:

var rows = [];
for (var i = 0; i < numrows; i++) {
    rows.push(ObjectRow());
}
return tbody(rows);

Вы можете использовать в основном ту же структуру при работе с JSX:

var rows = [];
for (var i = 0; i < numrows; i++) {
    // note: we add a key prop here to allow react to uniquely identify each
    // element in this array. see: https://reactjs.org/docs/lists-and-keys.html
    rows.push(<ObjectRow key={i} />);
}
return <tbody>{rows}</tbody>;

Между прочим, мой пример JavaScript почти точно то, во что превращается этот пример JSX. Поиграйте с Babel REPL, чтобы понять, как работает JSX.

1164
temporary_user_name 29 Янв 2019 в 01:21

Вы также можете использовать функцию самовызвания:

return <tbody>
           {(() => {
              let row = []
              for (var i = 0; i < numrows; i++) {
                  row.push(<ObjectRow key={i} />)
              }
              return row

           })()}
        </tbody>
8
Phillip 26 Сен 2017 в 21:28

Использование функции Карта массива - это очень распространенный способ перебирать элементы массива и создавать компоненты в соответствии с ними в React , это отличный способ сделать цикл, который является довольно эффективным и аккуратным способом сделать ваши циклы в JSX , это не единственный способ сделать это, но предпочтительный способ.

Кроме того, не забывайте иметь уникальный ключ для каждой итерации, как требуется. Функция карты создает уникальный индекс из 0, но не рекомендуется использовать созданный индекс, но если ваше значение уникально или если есть уникальный ключ, вы можете использовать их:

<tbody>
  {numrows.map(x=> <ObjectRow key={x.id} />)}
</tbody>

Кроме того, несколько строк из MDN , если вы не знакомы с функцией карты в массиве:

map вызывает предоставленную функцию обратного вызова один раз для каждого элемента в массиве по порядку и создает новый массив из результатов. обратный вызов вызывается только для индексов массива, которым присвоены значения, в том числе неопределенные. Он не вызывается для отсутствующих элементов массива (то есть индексов, которые никогда не были установлены, которые были удалены или которым никогда не было присвоено значение).

обратный вызов вызывается с тремя аргументами: значением элемента, индексом элемента и объектом Array, по которому осуществляется обход.

Если для сопоставления предоставлен параметр thisArg, он будет использоваться как обратное значение this. В противном случае значение undefined будет использоваться в качестве значения this. Это значение, в конечном итоге наблюдаемое обратным вызовом, определяется в соответствии с обычными правилами определения этого, видимого функцией.

map не изменяет массив, в котором он вызывается (хотя обратный вызов, если вызван, может сделать это).

78
Alireza 4 Мар 2019 в 07:54

Я знаю, что это старая ветка, но вы можете проверить React Templates, которая позволяет вы используете шаблоны типа jsx в реакции с несколькими директивами (такими как rt-repeat).

Ваш пример, если бы вы использовали ответные шаблоны, был бы:

<tbody>
     <ObjectRow rt-repeat="obj in objects"/>
</tbody>
31
Rich Warrior 14 Авг 2019 в 09:01

Вот пример из документа React: Выражения JavaScript как дети

function Item(props) {
  return <li>{props.message}</li>;
}

function TodoList() {
  const todos = ['finish doc', 'submit pr', 'nag dan to review'];
  return (
    <ul>
      {todos.map((message) => <Item key={message} message={message} />)}
    </ul>
  );
}

Как ваш случай, я предлагаю написать так:

function render() {
  return (
    <tbody>
      {numrows.map((roe, index) => <ObjectRow key={index} />)}
    </tbody>
  );
}

Обратите внимание, что ключ очень важен, потому что React использует ключ для различения данных в массиве.

9
Rich Warrior 7 Авг 2019 в 20:06

Я использую это как

<tbody>
  { numrows ? (
     numrows.map(obj => { return <ObjectRow /> }) 
    ) : null
  }
</tbody>
9
ravibagul91 15 Сен 2019 в 09:48

Вы, конечно, можете решить с помощью .map, как подсказывает другой ответ. Если вы уже используете babel, вы можете подумать об использовании jsx-control-Statement Они требуют небольшой настройки, но я думаю, что это стоит с точки зрения читабельности (особенно для не реагирующего разработчика). Если вы используете линтер, есть также eslint-plugin-jsx-control-Statement

12
Jurgo Boemo 26 Сен 2016 в 09:26

Я использую это:

gridItems = this.state.applications.map(app =>
          <ApplicationItem key={app.Id} app={app } />
);

PS: никогда не забывайте ключ, иначе у вас будет много предупреждений!

15
Kate Orlova 23 Мар 2020 в 16:57

Есть несколько способов сделать это. JSX в конечном итоге компилируется в JavaScript, поэтому, пока вы пишете правильный JavaScript, вы будете в порядке.

Мой ответ нацелен на объединение всех чудесных способов, уже представленных здесь:

Если у вас нет массива объектов, просто количество строк:

В блоке return создайте Array и используйте Array.prototype.map:

render() {
  return (
    <tbody>
      {Array(numrows).fill(null).map((value, index) => (
        <ObjectRow key={index}>
      ))}
    </tbody>
  );
}

Вне блока return просто используйте обычный цикл for для JavaScript:

render() {
  let rows = [];
  for (let i = 0; i < numrows; i++) {
    rows.push(<ObjectRow key={i}/>);
  } 
  return (
    <tbody>{rows}</tbody>
  );
}

Сразу вызывается функция выражения:

render() {
  return (
    <tbody>
      {() => {
        let rows = [];
        for (let i = 0; i < numrows; i++) {
          rows.push(<ObjectRow key={i}/>);
        }
        return rows;
      }}
    </tbody>
  );
}

Если у вас есть массив объектов

В блоке return .map() каждый объект компонента <ObjectRow>:

render() {
  return (
    <tbody>
      {objectRows.map((row, index) => (
        <ObjectRow key={index} data={row} />
      ))}
    </tbody>
  );
}

Вне блока return просто используйте обычный цикл for для JavaScript:

render() {
  let rows = [];
  for (let i = 0; i < objectRows.length; i++) {
    rows.push(<ObjectRow key={i} data={objectRows[i]} />);
  } 
  return (
    <tbody>{rows}</tbody>
  );
}

Сразу вызывается функция выражения:

render() {
  return (
    <tbody>
      {(() => {
        const rows = [];
        for (let i = 0; i < objectRows.length; i++) {
          rows.push(<ObjectRow key={i} data={objectRows[i]} />);
        }
        return rows;
      })()}
    </tbody>
  );
}
24
Yangshun Tay 15 Мар 2019 в 18:26

Не уверен, что это будет работать в вашей ситуации, но часто Карта - хороший ответ.

Если это был ваш код с циклом for:

<tbody>
    for (var i=0; i < objects.length; i++) {
        <ObjectRow obj={objects[i]} key={i}>
    } 
</tbody>

Вы можете написать это так с map :

<tbody>
    {objects.map(function(object, i){
        return <ObjectRow obj={object} key={i} />;
    })}
</tbody>

Синтаксис ES6:

<tbody>
    {objects.map((object, i) => <ObjectRow obj={object} key={i} />)}
</tbody>
849
Remco Haszing 5 Мар 2018 в 14:41

Вот простое решение для этого.

var Object_rows=[];
for (var i=0; i < numrows; i++) {
    Object_rows.push(<ObjectRow/>)
} 
<tbody>
  {Object_rows}
</tbody>

Нет необходимости в отображении и сложном коде. Вам просто нужно поместить строки в массив и вернуть значения для его визуализации.

10
ravibagul91 15 Сен 2019 в 09:47

Возможность ES2015 / Babel - использовать функцию генератора для создания массива JSX:

function* jsxLoop(times, callback)
{
    for(var i = 0; i < times; ++i)
        yield callback(i);
}

...

<tbody>
    {[...jsxLoop(numrows, i =>
        <ObjectRow key={i}/>
    )]}
</tbody>
15
David Q Hogan 28 Дек 2016 в 15:47

Просто используйте метод map Array с синтаксисом ES6:

<tbody>
  {items.map(item => <ObjectRow key={item.id} name={item.name} />)} 
</tbody>

Не забудьте свойство key.

81
danielfeelfine 30 Окт 2016 в 04:35

ES2015 Array.from с функцией карты + клавиша

Если у вас нет ничего для .map(), вы можете использовать Array.from() с функцией map для повторения элементов:

<tbody>
  {Array.from({ length: 5 }, (value, key) => <ObjectRow key={key} />)}
</tbody>
14
Jee Mok 16 Сен 2019 в 08:10

Я склоняюсь к подходу, где логика программирования происходит за пределами возвращаемого значения render. Это помогает сохранить то, что на самом деле легко сделать.

Так что я бы, наверное, сделал что-то вроде:

import _ from 'lodash';

...

const TableBody = ({ objects }) => {
  const objectRows = objects.map(obj => <ObjectRow object={obj} />);      

  return <tbody>{objectRows}</tbody>;
} 

По общему признанию, это такой небольшой объем кода, что его вставка может работать нормально.

13
Adam Donahue 9 Мар 2018 в 22:18

Просто используйте .map(), чтобы пройтись по своей коллекции и вернуть <ObjectRow> элементы с реквизитом из каждой итерации.

Предполагая, что objects является массивом где-то ...

<tbody>
  { objects.map((obj, index) => <ObjectRow obj={ obj } key={ index }/> ) }
</tbody>
15
Joshua Michael Waggoner 25 Янв 2018 в 18:29

Вы также можете извлечь вне блока возврата:

render: function() {
    var rows = [];
    for (var i = 0; i < numrows; i++) {
        rows.push(<ObjectRow key={i}/>);
    } 

    return (<tbody>{rows}</tbody>);
}
38
Brandon Sibeitokgong 2 Июн 2017 в 03:02

Ваш JSX-код будет скомпилирован в чистый код JavaScript, любые теги будут заменены ReactElement объектами. В JavaScript вы не можете вызывать функцию несколько раз для сбора возвращаемых переменных.

Это недопустимо, единственный способ - использовать массив для хранения возвращаемых функцией переменных.

Или вы можете использовать Array.prototype.map который доступен начиная с JavaScript ES5 чтобы справиться с этой ситуацией.

Возможно, мы сможем написать другой компилятор для воссоздания нового синтаксиса JSX для реализации функции повтора, например, Angular's ng-repeat.

12
Emile Bergeron 24 Окт 2016 в 03:42

Есть несколько ответов, указывающих на использование оператора map. Вот полный пример использования итератора в компоненте FeatureList для перечисления компонентов Feature на основе структуры данных JSON под названием features .

const FeatureList = ({ features, onClickFeature, onClickLikes }) => (
  <div className="feature-list">
    {features.map(feature =>
      <Feature
        key={feature.id}
        {...feature}
        onClickFeature={() => onClickFeature(feature.id)}
        onClickLikes={() => onClickLikes(feature.id)}
      />
    )}
  </div>
); 

Вы можете просмотреть полный код FeatureList на GitHub. , Список функций здесь.

20
Phillip 26 Сен 2017 в 20:48

Скажем, у нас есть массив элементов в вашем состоянии:

[{name: "item1", id: 1}, {name: "item2", id: 2}, {name: "item3", id: 3}]

<tbody>
    {this.state.items.map((item) => {
        <ObjectRow key={item.id} name={item.name} />
    })} 
</tbody>
16
Jan Franz Palngipang 20 Авг 2016 в 00:15

Чтобы выполнить цикл несколько раз и выполнить возврат, вы можете выполнить его с помощью from и map:

<tbody>
  {
    Array.from(Array(i)).map(() => <ObjectRow />)
  }
</tbody>

Где i = number of times


Если вы хотите назначить уникальные идентификаторы ключей для визуализируемых компонентов, вы можете использовать React.Children.toArray, как это предлагается в Реактивная документация

< Сильный > React.Children.toArray

Возвращает дочернюю непрозрачную структуру данных в виде плоского массива с ключами, назначенными каждому дочернему элементу. Полезно, если вы хотите манипулировать коллекциями дочерних элементов в ваших методах рендеринга, особенно если вы хотите переупорядочить или нарезать this.props.children перед его передачей.

< Сильный > Примечание :

React.Children.toArray() изменяет ключи, чтобы сохранить семантику вложенных массивов при сведении списков дочерних элементов. То есть, toArray ставит префикс перед каждым ключом в возвращаемом массиве, так что ключ каждого элемента ограничивается входным массивом, содержащим его.

<tbody>
  {
    React.Children.toArray(
      Array.from(Array(i)).map(() => <ObjectRow />)
    )
  }
</tbody>
17
Jee Mok 25 Дек 2019 в 01:25

Если вы уже используете lodash, удобная функция _.times.

import React, { Component } from 'react';
import Select from './Select';
import _ from 'lodash';

export default class App extends Component {
    render() {
        return (
            <div className="container">
                <ol>
                    {_.times(3, i =>
                        <li key={i}>
                            <Select onSelect={this.onSelect}>
                                <option value="1">bacon</option>
                                <option value="2">cheez</option>
                            </Select>
                        </li>
                    )}
                </ol>
            </div>
        );
    }
}
50
mpen 19 Июн 2018 в 23:42

Если вы решите конвертировать это внутри метода return () метода render , проще всего будет использовать метод map () . Сопоставьте ваш массив с синтаксисом JSX с помощью функции map (), как показано ниже (используется синтаксис ES6 ).


Внутри родительского компонента :

<tbody>
   { objectArray.map(object => <ObjectRow key={object.id} object={object.value}>) }
</tbody>

Обратите внимание, что атрибут key добавлен к вашему дочернему компоненту. Если вы не указали ключевой атрибут, вы можете увидеть следующее предупреждение на своей консоли.

Предупреждение: каждый дочерний элемент в массиве или итераторе должен иметь уникальную «ключевую» опору.

Примечание. Одна распространенная ошибка, которую делают люди, заключается в использовании index в качестве ключа при итерации, использование index элемента в качестве ключа является антишаблоном, и вы можете прочитать о нем подробнее здесь. Короче говоря, если НЕ статический список, никогда не используйте index в качестве ключа.


Теперь в компоненте ObjectRow вы можете получить доступ к объекту из его свойств.

Внутри компонента ObjectRow

const { object } = this.props

Или

const object = this.props.object

Это должно извлечь вам объект, который вы передали из родительского компонента в переменную object в компоненте ObjectRow . Теперь вы можете выплевывать значения в этом объекте в соответствии с вашей целью.


Ссылки :

метод map () в Javascript

ECMAScript 6 или ES6

17
bharadhwaj 1 Мар 2020 в 18:22

Если numrows - это массив, и это очень просто.

<tbody>
   {numrows.map(item => <ObjectRow />)}
</tbody>

Тип данных массива в React намного лучше, массив может поддерживать новый массив и поддерживать фильтрацию, уменьшение и т. Д.

23
lulin 11 Янв 2017 в 09:49

Вы можете сделать что-то вроде:

let foo = [1,undefined,3]
{ foo.map(e => !!e ? <Object /> : null )}
8
Pang 2 Май 2017 в 02:51

... Или вы также можете подготовить массив объектов и сопоставить его с функцией, чтобы получить желаемый результат. Я предпочитаю это, потому что это помогает мне поддерживать хорошую практику кодирования без какой-либо логики в возвращении рендера.

render() {
const mapItem = [];
for(let i =0;i<item.length;i++) 
  mapItem.push(i);
const singleItem => (item, index) {
 // item the single item in the array 
 // the index of the item in the array
 // can implement any logic here
 return (
  <ObjectRow/>
)

}
  return(
   <tbody>{mapItem.map(singleItem)}</tbody>
  )
}
15
Phillip 26 Сен 2017 в 20:56

Поскольку вы пишете синтаксис Javascript внутри JSX-кода, вам необходимо заключить Javascript в фигурные скобки.

row = () => {
   var rows = [];
   for (let i = 0; i<numrows; i++) {
       rows.push(<ObjectRow/>);
   }
   return rows;
}
<tbody>
{this.row()}  
</tbody>
9
Phillip 26 Сен 2017 в 04:21

Отличный вопрос

Когда я хочу добавить определенное количество компонентов, я использую вспомогательную функцию.

Определите функцию, которая возвращает JSX:

const myExample = () => {
    let myArray = []
    for(let i = 0; i<5;i++) {
        myArray.push(<MyComponent/>)
    }
    return myArray
}

//... in JSX

<tbody>
    {myExample()}
</tbody>
8
Phillip 26 Сен 2017 в 20:47