在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>
这可以用多种方式完成。
- 如上所述,在
return
之前,将所有元素存储在数组中 -
循环内
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> );