<tbody>
for (var i=0; i < numrows; i++) {
<ObjectRow/>
}
</tbody>
我意识到并理解为什么这不是有效的
JSX
,因为JSX
映射到函数调用。但是,由于来自模板领域并且是JSX
的新手,所以我不确定如何实现上述目的(多次添加组件)。#1 楼
就像您只是在调用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 are adding 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的工作原理。
评论
可能编译器已更改,因为此示例似乎未在jsx编译器中进行编译,但是像下面在FakeRainBrigand的答案中那样使用map似乎可以正确编译。
–rav
2014年7月2日,11:44
我可以保证最后一个示例仍然可以在最新的JSX编译器上正确编译。
–索菲·阿尔珀特(Sophie Alpert)
2014年7月2日在16:56
这很好。 FakeRainBrigand的答案适用于简单的数组迭代,但是对于不能使用map的情况(例如,变量中没有存储集合),此答案是必须的。
–开尔文
2014年8月8日在16:25
React需要有效地更新dom,在这种情况下,父级应该为每个子级分配一个密钥。参考:facebook.github.io/react/docs/…
– qsun
16年1月2日在22:36
这个例子是可行的,但是它错过了一些重要的地方,例如键属性以及在React代码库中并未真正使用的代码样式。请将此答案stackoverflow.com/questions/22876978/loop-inside-react-jsx/…视为正确答案。
– Okonetchnikov
16-10-31在14:57
#2 楼
不确定这是否适合您的情况,但通常使用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>
ES6语法:
<tbody>
{objects.map((object, i) => <ObjectRow obj={object} key={i} />)}
</tbody>
评论
如果iteratee是数组,则Map更有意义。如果为数字,则for循环是合适的。
–耳语者
15年4月26日在18:40
{objects.map((o,i)=>
–扭曲
15年7月27日在10:24
+1。我什至将它与ul(无序列表)一起使用。
- {objects.map((object:string,i:number)=> {return
- {object} })}}
– Roberto C Navarro
2015年9月28日在20:10
这很棒,但是您不能映射到对象..因为我会用它来..in
–达蒙
16年1月15日在19:09
请记住,使用Object.keys时,键的顺序是任意的。我发现单独存储键的顺序效果很好,如果需要,还可以存储对象文字的顺序。这使我可以编写类似this.props.order.map((k)=>
–tgrrr
16年3月15日在8:04
#3 楼
如果您还没有像@FakeRainBrigand的答案这样的数组到map()
,并且想要内联,以便源布局对应于比@SophieAlpert的答案更接近输出的输出:使用ES2015(ES6)语法(传播和箭头函数)
http://plnkr.co/edit/mfqFWODVy8dKQQOkIEGV?p=preview
<tbody>
{[...Array(10)].map((x, i) =>
<ObjectRow key={i} />
)}
</tbody>
关于:使用Babel进行转译,其警告页面指出,传播需要
Array.from
,但目前(v5.8.23
)在传播实际Array
时似乎并非如此。我有一个文档问题需要澄清。但是使用或自行填充均需您自担风险。香草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()
解决方案中纠正了我的错误(请参阅修订)。key
在所有情况下,
key
attr减轻了开发版本的警告,但在子级中无法访问。如果希望子级中的索引可用,则可以传递一个额外的attr。请参阅列表和键进行讨论。评论
产量呢?当我们有生成器时,将元素推入中间数组是很丑陋的。做他们的工作?
– mpen
2015年9月12日下午5:58
@Mark我不知道生成器在这里真的适用。在JSX上下文中,此操作的关键是返回数组的表达式。因此,如果您打算以某种方式使用生成器,则无论如何都可能会对其进行扩展,并且它可能比此处基于ES2015的解决方案更为冗长。
– JMM
15年9月12日在11:56
这怎么更冗长?为什么JSX不接受任何可迭代的方法,而不仅仅是数组?
– mpen
15年9月12日在17:34
@Mark确实比我使用的(ES5)IIFE示例更冗长,但是比[... Array(x)]。map(()=>
– JMM
2015年9月12日19:49
@corysimmons很酷,感谢您提供有关fill()的信息。我现在记得,我犹豫的原因是关于ES规范中如何指示参数的可选性的问题(必须调查一下)。逗号只是删除数组元素的一种方法-在本例中为第一个。如果希望索引从要散布的数组的1..length而不是散布的数组的0..length-1开始,则可以执行此操作(因为省略的元素仅会影响数组的长度,所以不要具有相应的属性)。
– JMM
16-10-25在15:29
#4 楼
只需使用具有ES6语法的map Array方法即可:<tbody>
{items.map(item => <ObjectRow key={item.id} name={item.name} />)}
</tbody>
不要忘记
key
属性。评论
我添加了一个随机的'Math.random()'密钥而不是id,当在孩子内部使用setState时,它不能正常工作,知道为什么吗?
– Oliver D
19年12月19日在18:04
#5 楼
使用数组映射功能是在React中循环遍历元素数组并根据它们创建组件的一种非常常见的方法,这是进行循环的一种好方法,这是在JSX中进行循环的一种非常有效和整洁的方法,不是唯一的方法,而是首选的方法。此外,不要忘记根据需要为每个迭代都拥有唯一的Key。 Map函数从0开始创建一个唯一索引,但是不建议使用产生的索引,但是如果您的值是唯一的或存在唯一键,则可以使用它们:
<tbody>
{numrows.map(x=> <ObjectRow key={x.id} />)}
</tbody>
此外,MDN中的几行如果您不熟悉Array上的map函数:
map依次为
数组中的每个元素调用提供的回调函数,并从结果中构造一个新的数组。仅对具有已分配值(包括未定义的值)的数组索引进行调用callback
数组的丢失元素而调用它(即,从未设置过的索引,已被删除的索引或从未分配过值的索引)。调用了回调具有三个参数:元素的值,
元素的索引和要遍历的Array对象。
如果将thisArg参数提供给地图,它将用作
回调的这个值。否则,未定义的值将用作该值。最终可通过回调观察到的值是
,是根据通常的规则由函数确定的。
map不会使调用它的数组发生变化(尽管
/>回调(如果被调用,可以这样做)。
评论
Array#map不异步!
– philraj
17年8月17日在1:51
#6 楼
如果您已经在使用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>
);
}
}
评论
如果您不包括lodash之类的库,则Array.prototype.map可能是一个不错的选择。 developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/…。
–以赛亚·格雷
15年12月24日在18:28
@IsaiahGrey仅当您已经有一个数组时。有时您只想重复几次。
– mpen
15年12月24日在18:29
当然可以!可以根据实际数据确定很多不同的方法。我发现,在开发过程中,我们经常能够适应地图,这是因为如何建模数据。使用lodash的好例子!顺便说一句,您是否正在使用属性初始化程序语法将方法绑定到组件?
–以赛亚·格雷
15年12月24日在18:33
@IsaiahGrey我认为它们只是被称为ES6方法定义
– mpen
16-2-22在16:39
#7 楼
您还可以提取返回块之外的内容:render: function() {
var rows = [];
for (var i = 0; i < numrows; i++) {
rows.push(<ObjectRow key={i}/>);
}
return (<tbody>{rows}</tbody>);
}
#8 楼
我知道这是一个旧线程,但是您可能想检出React模板,它允许您在React中使用jsx样式的模板,并带有一些指令(例如rt-repeat)。您的例如,如果您使用react-templates,则将是:
<tbody>
<ObjectRow rt-repeat="obj in objects"/>
</tbody>
评论
为什么当普通的javascript已经提供了带有各种for循环或Array.prototype.map方法的解决方案来解决问题时,为什么还要使用附加库?我自己在我当前的项目中已经使用了两种简单的javascript方式,都可以正常工作。尽可能使用简单的javascript方法的习惯有助于我提高javascript的技能。我仅在似乎很难找到某些问题的解决方案(例如使用React-Router进行路由)的情况下才使用其他库。
– Lex Soft
19年3月15日在14:19
#9 楼
有多种方法可以做到这一点。 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>
{(() => {
const rows = [];
for (let i = 0; i < objectRows.length; i++) {
rows.push(<ObjectRow key={i} data={objectRows[i]} />);
}
return rows;
})()}
</tbody>
);
}
评论
好的答案:各种技术都只使用普通的javascript,这显示了javascript的强大功能,这要归功于其执行类似任务的各种方式。
– Lex Soft
19年3月15日在14:59
#10 楼
如果numrows是一个数组,它非常简单。<tbody>
{numrows.map(item => <ObjectRow />)}
</tbody>
React中的数组数据类型更好,数组可以支持新数组,并支持过滤器,reduce等。
评论
这不是一个体面的答案,因为它没有使用键。
–kojow7
17/09/20在21:55
#11 楼
有几个答案指向使用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>
);
您可以在上查看完整的FeatureList代码。 GitHub。功能装置列在此处。
评论
当处理JSON数据时,例如使用fetch API从数据库中检索数据时,我依靠使用Array.prototype.map方法。这是一种方便且经过验证的方法。
– Lex Soft
19 Mar 15 '19在14:32
#12 楼
要循环多次并返回,可以在from
和map
的帮助下实现:<tbody>
{
Array.from(Array(i)).map(() => <ObjectRow />)
}
</tbody>
其中
i = number of times
如果要将唯一的密钥ID分配给渲染的组件后,您可以使用React文档中建议的
React.Children.toArray
React.Children.toArray
将子级不透明数据结构作为平面数组返回,并为每个子级分配键。如果要在渲染方法中操纵子级的集合,尤其是在要向下传递this.props.children的顺序或对其进行切片时,尤其有用。
注意:
React.Children.toArray()
将键更改为展平子列表时,保留嵌套数组的语义。也就是说,toArray在返回的数组中的每个键之前加上前缀,以便每个元素的键都在包含它的输入数组中作用域。<tbody>
{
React.Children.toArray(
Array.from(Array(i)).map(() => <ObjectRow />)
)
}
</tbody>
#13 楼
假设我们在您的状态下有一系列商品:[{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>
评论
我认为您可能需要摆脱map(item)之后的{},这似乎对我有用。
–伊恩
16 Dec 10'在17:22
#14 楼
如果您选择在render方法的return()内部进行转换,最简单的选择就是使用map()方法。如下所示,使用map()函数将数组映射为JSX语法(使用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
这应该获取您从父组件传递到的对象ObjectRow组件中的变量
object
。现在您可以根据自己的目的吐出该对象中的值。参考文献:Javascript中的
map()方法
ECMA Script 6或ES6
#15 楼
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>
#16 楼
带映射功能+键的ES2015 Array.from如果对
.map()
没什么了解,可以将Array.from()
与map
函数一起使用以重复元素:<tbody>
{Array.from({ length: 5 }, (value, key) => <ObjectRow key={key} />)}
</tbody>
#17 楼
我用这个:gridItems = this.state.applications.map(app =>
<ApplicationItem key={app.Id} app={app } />
);
PS:永远不要忘记按键,否则您会收到很多警告!
评论
如果您的商品没有.Id属性,请使用数组索引,例如items.map((item,index)=>
– kontur
17-3-30在9:26
#18 楼
...或者您也可以准备一个对象数组并将其映射到一个函数以获取所需的输出。我更喜欢这样做,因为它可以帮助我保持良好的编码习惯,并且在返回的渲染中没有任何逻辑。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>
)
}
#19 楼
只需使用.map()
遍历您的集合,并从每次迭代中返回带有道具的<ObjectRow>
项目。假设
objects
是某个数组,则位于某处... <tbody>
{ objects.map((obj, index) => <ObjectRow obj={ obj } key={ index }/> ) }
</tbody>
#20 楼
这可以通过多种方式完成。如上所建议,在
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
* 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>
)
评论
是的在我当前的项目中,我使用方法1,即使用Array.prototype,forEach()方法创建HTML select元素,该元素由数据库中的数据填充。但是,由于map方法看起来更紧凑(代码更少),因此我更可能将它们替换为Array.prototype.map()方法。
– Lex Soft
19年3月15日在15:08
#21 楼
我倾向于采用一种编程逻辑发生在render
的返回值之外的方法。这有助于使实际呈现的内容易于理解。所以我可能会做类似的事情:
import _ from 'lodash';
...
const TableBody = ({ objects }) => {
const objectRows = objects.map(obj => <ObjectRow object={obj} />);
return <tbody>{objectRows}</tbody>;
}
诚然,这是一个内联它的少量代码可能很好用。
评论
Lodash地图的忠实粉丝,用于遍历对象。我倾向于从'lodash'导入{map},因此即使您不需要它们也不导入所有lodash函数。
– Stretch0
18 Mar 8 '18 at 11:11
如今,我们甚至不需要lodash映射-只需直接在数组上映射即可。
–亚当·多纳休(Adam Donahue)
18 Mar 9 '18 at 22:19
是的,但是您无法使用es6 map()循环访问对象,只能使用数组。那就是我所说的lodash有益于循环对象。
– Stretch0
18 Mar 12 '18 at 9:26
我以为你的意思是指事物的存在,这是我的错。
–亚当·多纳休(Adam Donahue)
18年3月13日在13:24
#22 楼
您当然可以使用其他答案建议的.map进行求解。如果您已经使用babel,则可以考虑使用jsx-control-statements,它们需要一些设置,但我认为从可读性角度考虑是值得的(特别是对于没有反应的开发人员)。
如果您使用皮棉机,还有eslint-plugin-jsx-control-statements
#23 楼
您的JSX代码将编译为纯JavaScript代码,所有标签都将替换为ReactElement
对象。在JavaScript中,不能多次调用一个函数来收集其返回的变量。这是非法的,唯一的方法是使用数组存储该函数返回的变量。
或者您可以使用从JavaScript ES5开始提供的
Array.prototype.map
来处理这种情况。我们可以编写其他编译器来重新创建新的JSX语法以实现重复功能,就像Angular的
ng-repeat
一样。#24 楼
这是一个简单的解决方案。 var Object_rows = [];
for (var i = 0; i < numrows; i++) {
Object_rows.push(<ObjectRow />);
}
<tbody>{Object_rows}</tbody>;
不需要映射和复杂的代码。您只需要将行压入数组并返回值即可呈现它。
评论
创建html select元素时,这种相似的技术首先是我想到的,因此尽管使用了forEach()方法,但还是使用了它。但是,当我重新阅读有关列表和键的主题的React文档时,我发现它们使用map()方法,如此处的几个答案所示。这使我认为这是首选方式。我同意,因为它看起来更紧凑(代码更少)。
– Lex Soft
19 Mar 15 '19在15:20
#25 楼
由于您是在JSX代码中编写Javascript语法,因此需要将Javascript括在花括号中。row = () => {
var rows = [];
for (let i = 0; i<numrows; i++) {
rows.push(<ObjectRow/>);
}
return rows;
}
<tbody>
{this.row()}
</tbody>
#26 楼
您还可以使用自调用功能:return <tbody>
{(() => {
let row = []
for (var i = 0; i < numrows; i++) {
row.push(<ObjectRow key={i} />)
}
return row
})()}
</tbody>
评论
在render中使用匿名函数不被视为一种好的响应方式,因为这些函数需要在每次重新渲染时重新创建或丢弃。
– Vlatko Vlahek
17年9月29日在7:25
@VlatkoVlahek您会误以为它会在每个渲染周期中重新创建功能道具,这可能会导致大规模的性能下降。在其他任何地方创建匿名函数都不会以任何重大方式影响性能。
– Emile Bergeron
2月20日下午16:54
@EmileBergeron这是前一段时间哈哈。我同意,如果不将其用作道具,没有区别。
– Vlatko Vlahek
2月20日18:36
#27 楼
这是来自React doc的示例: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使用键来更改数组中的数据。
#28 楼
我像<tbody>
{ numrows ? (
numrows.map(obj => { return <ObjectRow /> })
) : null
}
</tbody>
这样使用
#29 楼
您可以执行以下操作:let foo = [1,undefined,3]
{ foo.map(e => !!e ? <Object /> : null )}
#30 楼
很好的问题。要添加一定数量的组件时该怎么做,就是使用辅助函数。
定义一个返回JSX的函数:
const myExample = () => {
let myArray = []
for(let i = 0; i<5;i++) {
myArray.push(<MyComponent/>)
}
return myArray
}
//... in JSX
<tbody>
{myExample()}
</tbody>
评论
请务必注意,在JSX中,您需要在JavaScript语法周围加上{}标签。这可能会帮助facebook.github.io/react/docs/…。let todos = this.props.todos.map((todo)=> {返回
{todo.title}
})相关:如何渲染重复的React元素?
@OverCoder为什么将整个return放入{}标记中,这将是=> return
{todo.title}
是吗?@pravinpoudel实际上这个答案很旧,更像是let todos = this.props.todos.map(t =>
{t.title}
):)