在React JSX中循环

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

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

我意识到并理解为什么这是不合法的JSX,因为JSX映射到函数调用。 但是,来自模板领域和JSX的新手,我不确定如何实现上述(多次添加组件)。

想想你就是在调用JavaScript函数。 你不能在函数调用中放置一个for循环:

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

但是你可以创build一个数组,然后通过它:

 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++) { rows.push(<ObjectRow />); } return <tbody>{rows}</tbody>; 

顺便说一句,我的JavaScript例子几乎就是JSX转换成的例子。 玩Babel REPL来体验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> 

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

使用ES2015(ES6)语法(扩展和箭头函数)

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

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

Re:和Babel一起编译,其警告页面说Array.from是传播所必需的,但是目前( v5.8.23 ),传播一个真正的Array并不是这种情况。 我有一个文档问题,以澄清这一点。 但是请自担风险或使用polyfill。

香草ES5

Array.apply

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

内联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()解决scheme的早期版本(参见修订版)中纠正我的错误。

key

在所有情况下, key attr缓解与发展build设的警告,但不能在儿童访问。 如果您希望子项中的索引可用,则可以传递额外的attr。 参见列表和键进行讨论。

使用ES6语法简单地使用map Array方法:

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

不要忘记key财产。

如果你已经在使用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风格的模板,有一些指令(比如rt-重复)。

你的例子,如果你使用react-templates,将是:

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

使用数组映射函数是非常常见的方式来循环通过一个数组元素,并根据它们在React中创build组件,这是一个很好的方式来做一个循环,这是一个非常有效和整洁的方式来做你的循环在JSX中 ,这不是唯一这样做的方式,但更喜欢的方式。

另外不要忘记根据需要为每个迭代设置一个唯一的密钥。 Map函数从0开始创build一个唯一的索引,但是它不是使用索引build议的,但是如果你的值是唯一的,或者如果有一个唯一的键,你可以使用它们:

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

如果您不熟悉Array上的map函数,那么还有几行来自MDN

map按顺序为数组中的每个元素调用一次提供的callback函数,并从结果中构造一个新的数组。 只有已经赋值的数组的索引才会被调用,包括未定义的。 它不会被调用缺less的数组元素(也就是说,从来没有被设置过的索引,这些索引已经被删除,或者从来没有被赋值过)。

使用三个参数调用callback:元素的值,元素的索引和被遍历的Array对象。

如果提供了一个thisArg参数来映射,它将被用作callback的这个值。 否则,未定义的值将被用作它的这个值。 通过callback最终可观察到的这个值是根据通常的规则来确定的,以确定函数所看到的。

map不会改变它被调用的数组(尽pipecallback,如果调用,可能会这样做)。

如果numrows是一个数组,并且非常简单。

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

React中的数组数据types非常好,数组可以返回新数组,并支持过滤,减less等。

有几个答案指向使用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代码 。 这里列出了特征夹具 。

让我们说我们在你的状态有一系列的项目:

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

如果你select将这个内部的render ()方法转换成return ,那么最简单的select就是使用map()方法。 使用map()函数将数组映射到JSX语法,如下所示(使用ES6语法 )。


在父组件内部

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

请注意添加到您的子组件的key属性。 如果您没有提供关键属性,则可以在控制台上看到以下警告。

警告:数组或迭代器中的每个孩子应该有一个唯一的“键”支柱。


现在,在ObjectRow组件中,您可以从其属性中访问该对象。

里面的ObjectRow组件

const { object } = this.props

要么

const object = this.props.object

这应该把你从父组件传递给ObjectRow组件中的variablesobject 。 现在你可以根据你的目的吐出那个对象的值。


参考文献

Javascript中的map()方法

ECMA脚本6或ES6

ES2015 / Babel可能性是使用生成器函数来创build一个JSX数组:

 function* jsxLoop(times, callback) { for(var i = 0; i < times; ++i) yield callback(i); } ... <tbody> {[...jsxLoop(numrows, i => <ObjectRow key={i}/> )]} </tbody> 

我使用这个:

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

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

你的JSX代码将被编译成纯JavaScript代码,任何标签都将被ReactElement对象所取代。 在JavaScript中,您不能多次调用函数来收集其返回的variables。

这是非法的,唯一的方法是使用数组来存储函数返回的variables。

或者你可以使用自JavaScript ES5以来可用的Array.prototype.map来处理这种情况。

也许我们可以编写其他编译器来重新创build一个新的JSX语法来实现像Angular的ng-repeat一样的重复函数。

我倾向于赞成编程逻辑发生在render返回值之外的方法。 这有助于保持实际上易于理解的内容。

所以我可能会做这样的事情:

 import _ from 'lodash'; ... const TableBody = ({objects}) => { const objectRows = _.map(objects, obj => <ObjectRow object={obj} />); return <tbody>{objectRows}</tbody>; } 

无可否认,这是less量的内联代码,可能会正常工作。

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

 row = () => { var rows = []; for (let i = 0; i<numrows; i++) { rows.push(<ObjectRow/>); } return rows; } <tbody> {this.row()} </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 * eg 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> ) 

我喜欢用它

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

这是一个简单的解决scheme。

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

没有映射和复杂的代码required.You只需要推行到数组,并返回值来呈现它。

你可以做一些事情:

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

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

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

以下是来自React文档的示例: 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> ); } 

作为你的情况,我build议这样写:

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

请注意Key是非常重要的,因为React使用Key来区分数组中的数据。

有很多方法可以做到这一点。 JSX最终会被编译成JavaScript,所以只要你写了有效的JavaScript,你就会很好。

我的答案旨在巩固已经提出的所有美妙的方式:

如果你没有一个对象数组,简单的行数:

return块中,创build一个Array并使用Array.prototype.map

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

return块外部,只需使用普通的JavaScript for-loop:

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

立即调用函数expression式:

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

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

立即调用函数expression式:

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

你会想要添加元素到一个数组,并呈现元素的数组。
这可以帮助减less重新渲染组件所需的时间。

以下是一些可能有所帮助的粗略代码:

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

你可以使用一个IIFE,如果你真的想真的在JSX内使用循环。

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

你也可以使用一个自我调用函数:

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