Петля внутри React JSX

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

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

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

30 ответов


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

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

но вы можете сделать массив, а затем передать это:

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. Поиграйте с Бабель расх чтобы почувствовать, как JSX завод.


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

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

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

вы могли бы написать его вот так карта:

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

если у вас еще нет массива для map() как ответ @FakeRainBrigand и хотите встроить это, чтобы исходный макет соответствовал выходу ближе, чем ответ @SophieAlpert:

с синтаксисом ES2015 (ES6) (функции распространения и стрелки)

http://plnkr.co/edit/mfqFWODVy8dKQQOkIEGV?p=preview

<tbody>
  {[...Array(10)].map((x, i) =>
    <ObjectRow key={i} />
  )}
</tbody>

Ре: transpiling с Бабелем, его предостережения страницы говорит, что Array.from требуется для распространения, но в настоящее время (v5.8.23) это, похоже, не так при распространении фактического Array. У меня есть выпуск документации открыть, чтобы уточнить это. Но используйте на свой страх и риск.

ваниль в ES5

Array.apply

<tbody>
  {Array.apply(0, Array(10)).map(function (x, i) {
    return <ObjectRow key={i} />;
  })}
</tbody>

Inline IIFE

http://plnkr.co/edit/4kQjdTzd4w69g8Suu2hT?p=preview

<tbody>
  {(function (rows, i, len) {
    while (++i <= len) {
      rows.push(<ObjectRow key={i} />)
    }
    return rows;
  })([], 0, 10)}
</tbody>

сочетание методов из других ответов

держать источник макет, соответствующий выходу, но сделать встроенную часть более компактной:

render: function () {
  var rows = [], i = 0, len = 10;
  while (++i <= len) rows.push(i);

  return (
    <tbody>
      {rows.map(function (i) {
        return <ObjectRow key={i} index={i} />;
      })}
    </tbody>
  );
}

С синтаксисом ES2015 & Array методы

С Array.prototype.fill вы можете сделать это в качестве альтернативы использованию спреда, как показано выше:

<tbody>
  {Array(10).fill(1).map((el, i) =>
    <ObjectRow key={i} />
  )}
</tbody>

(я думаю, вы действительно можете опустить любой аргумент fill(), но я не на 100% в этом уверен.) Спасибо @FakeRainBrigand за исправление моей ошибки в более ранней версии fill() решение (см. переделки.)

key

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


просто используя карта массив метод с синтаксисом ES6:

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

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


С помощью массив картой функция очень распространенный способ петли через массив элементов и создавать компоненты в соответствии с ними в реагировать, Это отличный способ сделать цикл, который довольно эффективный и аккуратный способ сделать ваши петли в JSX, Это не единственный способ сделать это, но предпочтительный способ.

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

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

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

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

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

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

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


Если вы уже используете 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>
        );
    }
}

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

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

    return (<tbody>{rows}</tbody>);
}

Я знаю, что это старый поток, но вы можете проверить http://wix.github.io/react-templates/, что позволяет использовать шаблоны в стиле jsx в react с несколькими директивами (например, RT-repeat).

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

<tbody>
     <ObjectRow rt-repeat="obj in objects"/>
</tbody>

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

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

тип данных массива в React очень лучше, массив может поддержать новый массив, и фильтр поддержки, уменьшает etc.


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

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 code на GitHub. The особенности приспособления перечислены здесь.


допустим у нас есть массив элементов в вашем государстве:

[{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>

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


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

<tbody> { objectArray.map((object, index) => <ObjectRow key={index} object={object}>) } </tbody>

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

предупреждение: каждый ребенок в массиве или итераторе должен иметь уникальная" ключевая " опора.


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

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

const { object } = this.props

или

const object = this.props.object

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


ссылки :

метод map () в Javascript

ECMA Script 6 или ES6


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

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

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


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

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

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

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

<tbody>
  { objects.map((obj, index) => <ObjectRow obj={ obj } key={ index }/> ) }
</tbody>

возможность 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>

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


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

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

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

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


ES2015 массив.от С функцией карты + ключ

если у вас нет ничего .map() можно использовать Array.from() С mapFn повторить элементы:

<tbody>
  {Array.from({ length: 5 }, (v, k) => <ObjectRow key={k} />)}
</tbody>

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

поэтому я, вероятно, сделаю что-то вроде:

import _ from 'lodash';

...

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

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

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


Это можно сделать несколькими способами.

  1. как было предложено выше, перед return хранить все элементы в массиве
  2. петли внутри return

    Способ 1

     let container =[];
        let arr = [1,2,3] //can be anything array, object 
    
        arr.forEach((val,index)=>{
          container.push(<div key={index}>
                         val
                         </div>)
            /** 
            * 1. All loop generated elements require a key 
            * 2. only one parent element can be placed in Array
            * e.g. container.push(<div key={index}>
                                        val
                                  </div>
                                  <div>
                                  this will throw error
                                  </div>  
                                )
            **/   
        });
        return (
          <div>
             <div>any things goes here</div>
             <div>{container}</div>
          </div>
        )
    

    Способ 2

       return(
         <div>
         <div>any things goes here</div>
         <div>
            {(()=>{
              let container =[];
              let arr = [1,2,3] //can be anything array, object 
              arr.forEach((val,index)=>{
                container.push(<div key={index}>
                               val
                               </div>)
                             });
                        return container;     
            })()}
    
         </div>
      </div>
    )
    

для цикла в течение нескольких раз и возврата, вы можете достичь этого с помощью from и map:

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

здесь i = number of times


Я использую его как

<tbody>
{ numrows ? (
   numrows.map(obj => { return <ObjectRow /> }) 
) : null}
</tbody>

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

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

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

внутри return блок, создавая Array и с помощью Array.prototype.map:

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

за пределами return блок, просто используйте обычный JavaScript для-loop:

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 блок, просто используйте обычный JavaScript для-loop:

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>
      {() => {
        let rows = [];
        for (let i = 0; i < objectRows.length; i++) {
          rows.push(<ObjectRow key={i} data={objectRows[i]} />);
        }
        return rows;
      }}
    </tbody>
  );
}

поскольку вы пишете синтаксис Javascript внутри кода JSX, вам нужно обернуть Javascript в фигурные скобки.

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

вот простое решение.

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

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


большой вопрос.

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

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

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

//... in JSX

<tbody>
    {myExample()}
</tbody>

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

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

           })()}
        </tbody>

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

let foo = [1,undefined,3]
{ foo.map(e => !!e ? <Object /> : null )}

вот пример из React doc: https://facebook.github.io/react/docs/jsx-in-depth.html#javascript-expressions-as-children

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

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