在React JSX中循环

rut 发布于 2019-05-09 javascript 最后更新 2019-05-09 19:33 37 浏览

我正在尝试在React JSX(其中ObjectRow是一个单独的组件)中执行以下操作:

<tbody>
    for (var i=0; i < numrows; i++) {
        <ObjectRow/>
    } 
</tbody>
我意识到并理解为什么这不是有效的JSX,因为JSX映射到函数调用。但是,来自模板领域并且是JSX的新手,我不确定如何实现上述目标(多次添加组件)。
已邀请:

qet

赞同来自:

这是一个简单的解决方案。

var Object_rows=[];
for (var i=0; i < numrows; i++) {
    Object_rows.push(<ObjectRow/>)
} 
<tbody>
{Object_rows}
</tbody>
不需要映射和复杂的代码。您只需要将行推送到数组并返回值来呈现它。

pnon

赞同来自:

如果你选择在render方法的return()内部转换它,最简单的选择就是使用map()方法。使用map()函数将数组映射到JSX语法,如下所示(使用ES6语法)。


内部父组件: <tbody> { objectArray.map((object, index) => <ObjectRow key={index} object={object}>) } </tbody> 请注意key属性已添加到您的子组件。如果未提供键属性,则可以在控制台上看到以下警告。
Warning: Each child in an array or iterator should have a unique "key" prop.

现在,在ObjectRow组件中,您可以从其属性访问该对象。 在ObjectRow组件内部 const { object } = this.props 要么 const object = this.props.object 这应该会将您从父组件传递的对象提取到ObjectRow组件中的变量object。现在,您可以根据目的吐出该对象中的值。
参考文献: map() method in Javascript ECMA Script 6 or ES6

gminus

赞同来自:

我倾向于赞成一种方法,其中编程逻辑发生在render的返回值之外。这有助于保持实际呈现的内容易于理解。 所以我可能会这样做:

import _ from 'lodash';
...
const TableBody = ({objects}) => {
  const objectRows = _.map(objects, obj => <ObjectRow object={obj} />);
return <tbody>{objectRows}</tbody>;
} 
不可否认,这是一个很少的代码,内联它可能会正常工作。

hid

赞同来自:

我喜欢它

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

trerum

赞同来自:

如果numrows是一个数组,而另一个回答最好的方法是map方法。如果没有,你只能从jsx访问它,你也可以使用这个ES6方法:

<tbody>
    { 
      [...Array(numrows).fill(0)].map((value,index)=><ObjectRow key={index} />)
    } 
</tbody>

pculpa

赞同来自:

由于您在JSX代码中编写Javascript语法,因此需要将Javascript包装在花括号中。

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

aet

赞同来自:

可以想象它就像你只是调用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转换的例子。使用Babel REPL来了解JSX的工作原理。

qquasi

赞同来自:

return (
        <table>
          <tbody>
          {
            numrows.map((item, index) => {
              <ObjectRow data={item} key={index}>
            })
          }
          </tbody>
        </table>
      );

vanimi

赞同来自:

您的JSX代码将编译为纯JavaScript代码,任何标记都将被ReactElement对象替换。在JavaScript中,您无法多次调用函数来收集返回的变量。 这是非法的,唯一的方法是使用数组来存储返回变量的函数。 或者您可以使用可用的Array.prototype.map since JavaScript ES5来处理这种情况。 也许我们可以编写其他编译器来重新创建一个新的JSX语法来实现重复函数,就像Angular's ng-repeat一样。

nodio

赞同来自:

这可以通过多种方式完成。

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

nvelit

赞同来自:

ES2015 Array.from,带有map函数+ key 如果您对.map()没有任何帮助,可以将Array.from()mapFn一起使用来重复元素:
<tbody>
  {Array.from({ length: 5 }, (v, k) => <ObjectRow key={k} />)}
</tbody>

quo_et

赞同来自:

不确定这是否适用于您的情况,但通常map是一个很好的答案。 如果这是你的代码与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>

jculpa

赞同来自:

你可以这样做:

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

taut

赞同来自:

...或者您也可以准备一个对象数组并将其映射到一个函数以获得所需的输出。我更喜欢这个,因为它有助于我保持编码的良好实践,在渲染的返回中没有逻辑。

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

kex

赞同来自:

如果numrows是一个数组,那很简单。

<tbody>
   {numrows.map(item => <ObjectRow />)}
</tbody>
React中的数组数据类型非常好,数组可以支持新数组,并支持过滤,减少等。

enemo

赞同来自:

只需使用.map()和ES6循环遍历您的集合,并使用每次迭代的道具返回<ObjectRow>项。

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

mnatus

赞同来自:

以下是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>
  );
}
请注意Key非常重要,因为React使用Key来区分数组中的数据。

id_aut

赞同来自:

如果你真的想在JSX中使用for循环,你可以使用IIFE。

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

qquasi

赞同来自:

您还可以使用自调用功能:

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

frerum

赞同来自:

如果您已经在使用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>
        );
    }
}

ynulla

赞同来自:

使用数组映射函数是循环通过一个元素数组并在React中根据它们创建组件的非常常见的方法,这是一个很好的方法来做一个循环,它是非常有效和整洁的方式在JSX中进行循环,它不是唯一的这样做的方式,但更喜欢的方式。搜索结果 另外,请不要忘记根据需要为每次迭代设置唯一的密钥。 Map函数从0创建一个唯一索引,但不是使用索引建议的,但如果您的值是唯一的,或者如果有唯一键,则可以使用它们:

<tbody>
  {numrows.map((x) => <ObjectRow key={x.id} />)}
</tbody>
如果您不熟悉Array上的map函数,那么来自MDN的行也很少:
map calls a provided callback function once for each element in an array, in order, and constructs a new array from the results. callback is invoked only for indexes of the array which have assigned values, including undefined. It is not called for missing elements of the array (that is, indexes that have never been set, which have been deleted or which have never been assigned a value). callback is invoked with three arguments: the value of the element, the index of the element, and the Array object being traversed. If a thisArg parameter is provided to map, it will be used as callback's this value. Otherwise, the value undefined will be used as its this value. The this value ultimately observable by callback is determined according to the usual rules for determining the this seen by a function. map does not mutate the array on which it is called (although callback, if invoked, may do so).

svelit

赞同来自:

有很多方法可以做到这一点。 JSX最终被编译为JavaScript,所以只要你编写有效的JavaScript,你就会很好。 我的回答旨在巩固已经呈现的所有精彩方式: 如果没有对象数组,只需要行数: 在return块中,创建一个Array并使用Array.prototype.map

render() {
  return (
    <tbody>
      {Array(numrows).fill(null).map((value, index) => (
        <ObjectRow key={index}>
      ))}
    </tbody>
  );
}
return块之外,只需使用普通的JavaScript for循环:
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 for循环:
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>
  );
}

cnisi

赞同来自:

我用这个:

gridItems = this.state.applications.map(app =>
                            <ApplicationItem key={app.Id} app={app } />
                            );
PD:永远不要忘记密钥,否则你会收到很多警告!

oiusto

赞同来自:

您还可以在返回块之外提取:

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

dmodi

赞同来自:

您需要向数组添加元素并呈现元素数组 这有助于减少重新渲染组件所需的时间。 这里有一些可能有用的粗略代码:

MyClass extends Component {
    constructor() {
        super(props)
        this.state = { elements: [] }
    }
    render() {
        return (<tbody>{this.state.elements}<tbody>)
    }
    add() {
        /*
         * The line below is a cheap way of adding to an array in the state.
         * 1) Add <tr> to this.state.elements
         * 2) Trigger a lifecycle update.
         */
        this.setState({
            elements: this.state.elements.concat([<tr key={elements.length}><td>Element</td></tr>])
        })
    }
}

dsed

赞同来自:

如果您还没有像@ FakeRainBrigand的答案那样有map()的数组,并且想要内联这样,那么源布局对应的输出比@ SophieAlpert的答案更接近:

使用ES2015(ES6)语法(扩展和箭头功能) http://plnkr.co/edit/mfqFWODVy8dKQQOkIEGV?p=preview
<tbody>
  {[...Array(10)].map((x, i) =>
    <ObjectRow key={i} />
  )}
</tbody>
Re:使用Babel进行转换,其caveats page表示Array.from是传播所必需的,但目前(v5.8.23)在传播实际的Array时似乎并非如此。我有一个documentation issue,以澄清这一点。但请自担风险或使用polyfill。

Vanilla 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。有关讨论,请参阅Lists and Keys

aet

赞同来自:

我知道这是一个旧线程,但您可能想要检查http://wix.github.io/react-templates/,它允许您使用jsx样式模板进行反应,并使用一些指令(例如rt-repeat)。 您的示例,如果您使用react-templates,将是:

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

tin

赞同来自:

好问题。 当我想添加一定数量的组件时,我所做的就是使用辅助函数。 定义一个返回JSX的函数:

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

sit_et

赞同来自:

让我们说你们州有一系列物品:

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

yet

赞同来自:

只需使用带有ES6语法的map Array方法:

<tbody>
  {items.map(item => <ObjectRow key={item.id} name={item.name} />)} 
</tbody>
不要忘记key属性。

desse

赞同来自:

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>

paut

赞同来自:

React元素是简单的JS,因此您可以遵循javascript规则。所以你可以在javascript中使用for循环,如下所示: -

<tbody>
    for (var i=0; i < numrows; i++) {
        <ObjectRow/>
    } 
</tbody>
但有效且最好的方法是使用.map函数。如下所示: -
<tbody>
    {listObject.map(function(listObject, i){
        return <ObjectRow key={i} />;
    })}
</tbody>
在这里,有一件事是必要的:定义密钥。否则会发出如下警告: -
warning.js:36 Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of ComponentName. See "link" for more information.

tiste

赞同来自:

有几个答案指向使用map语句。下面是一个完整的示例,它使用FeatureList组件中的迭代器,根据名为features的JSON数据结构列出Feature组件。

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>
); 
您可以在GitHub上查看完整的FeatureList codefeatures fixture is listed here

wet

赞同来自:

您当然可以使用.map解决,如其他答案所示。如果你已经使用了babel,你可以考虑使用jsx-control-statements 它们需要一些设置,但我认为它在可读性方面是值得的(特别是对于非反应开发人员)。 如果使用linter,还有eslint-plugin-jsx-control-statements