我正在尝试在 React JSX 中执行以下操作(其中 ObjectRow 是一个单独的组件):
<tbody>
for (var i=0; i < numrows; i++) {
<ObjectRow/>
}
</tbody>
我意识到并理解为什么这不是有效的 JSX,因为 JSX 映射到函数调用。但是,来自模板领域并且是 JSX 的新手,我不确定如何实现上述目标(多次添加组件)。
let todos = this.props.todos.map((todo) => {return <h1>{todo.title}</h1>})
let todos = this.props.todos.map(t => <h1>{t.title}</h1>)
:)
把它想象成你只是在调用 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 的工作原理。
我不确定这是否适合您的情况,但通常 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>
如果您还没有像@FakeRainBrigand 的答案那样的 map()
数组,并且想要内联它,那么源布局对应的输出比@SophieAlpert 的答案更接近:
使用 ES2015 (ES6) 语法(展开和箭头函数)
http://plnkr.co/edit/mfqFWODVy8dKQQOkIEGV?p=preview
<tbody>
{[...Array(10)].map((x, i) =>
<ObjectRow key={i} />
)}
</tbody>
回复:使用 Babel 进行编译,它的 caveats page 表示传播需要 Array.from
,但目前(v5.8.23
)在传播实际 Array
时似乎并非如此。我有一个 documentation issue 可以澄清这一点。但使用您自担风险或使用 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.prototype.fill
,您可以将其作为使用 spread 的替代方法,如上图所示:
<tbody>
{Array(10).fill(1).map((el, i) =>
<ObjectRow key={i} />
)}
</tbody>
(我认为您实际上可以省略 fill()
的任何参数,但我不是 100% 的。)感谢 @FakeRainBrigand 纠正了我在早期版本的 fill()
解决方案中的错误(请参阅修订版)。
钥匙
在所有情况下,key
attr 都会减轻开发构建的警告,但在子级中无法访问。如果您希望子级中的索引可用,则可以传递额外的 attr。请参阅 Lists and Keys 进行讨论。
简单地使用 ES6 语法的 map Array 方法:
<tbody>
{items.map(item => <ObjectRow key={item.id} name={item.name} />)}
</tbody>
不要忘记 key
属性。
在 React 中,使用 Array map 函数是循环遍历元素数组并根据它们创建组件的一种非常常见的方法。这是执行循环的好方法,它非常有效,并且是在 JSX 中执行循环的整洁方法。这不是唯一的方法,而是首选的方法。
此外,不要忘记根据需要为每次迭代都有一个唯一的密钥。 map 函数从 0 开始创建唯一索引,但不建议使用生成的索引,但如果您的值是唯一的或存在唯一键,则可以使用它们:
<tbody>
{numrows.map(x=> <ObjectRow key={x.id} />)}
</tbody>
另外,如果您不熟悉 Array 上的 map 函数,请参考 MDN 的几行代码:
map 按顺序为数组中的每个元素调用一次提供的回调函数,并根据结果构造一个新数组。回调仅针对具有赋值的数组索引调用,包括未定义的。对于数组的缺失元素(即从未设置、已删除或从未分配值的索引),不会调用它。 callback 使用三个参数调用:元素的值、元素的索引和被遍历的 Array 对象。如果向地图提供了 thisArg 参数,它将用作回调的 this 值。否则,值 undefined 将用作其 this 值。回调最终可观察到的这个值是根据用于确定函数看到的 this 的常用规则来确定的。 map 不会改变调用它的数组(尽管回调,如果被调用,可能会这样做)。
Array#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}>repeated 3 times</li>
))}
</ol>
</div>
);
}
}
times
,而不是整个库。在开发过程中可以避免在 Lodash 发布更新时向后不兼容,但在生产中,导入整个库需要花费几秒钟的时间。
lodash-es
才能使摇树工作,不是吗?
有多种方法可以做到这一点。 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>
);
}
您还可以在返回块之外提取:
render: function() {
var rows = [];
for (var i = 0; i < numrows; i++) {
rows.push(<ObjectRow key={i}/>);
}
return (<tbody>{rows}</tbody>);
}
您可能想查看 React Templates,它允许您在 React 中使用 JSX 样式的模板,并带有一些指令(例如 rt-repeat)。
您的示例,如果您使用 react-templates,将是:
<tbody>
<ObjectRow rt-repeat="obj in objects"/>
</tbody>
如果你选择在 render 方法的 return() 中转换它,最简单的选择是使用 map() 方法。使用 map() 函数将数组映射为 JSX 语法,如下所示(使用 ES6 语法)。
在父组件内部:
<tbody>
{ objectArray.map(object => <ObjectRow key={object.id} object={object.value} />) }
</tbody>
请注意 key
属性已添加到您的子组件中。如果您没有提供关键属性,您可以在控制台上看到以下警告。
警告:数组或迭代器中的每个孩子都应该有一个唯一的“key”道具。
注意:人们常犯的一个错误是在迭代时使用 index
作为键。使用元素的 index
作为键是一种反模式,您可以阅读有关它的更多信息 here。简而言之,如果它不是静态列表,则永远不要使用 index
作为键。
现在在 ObjectRow 组件中,您可以从其属性访问该对象。
在 ObjectRow 组件内部
const { object } = this.props
或者
const object = this.props.object
这应该会获取您从父组件传递到 ObjectRow 组件中的变量 object
的对象。现在您可以根据您的目的吐出该对象中的值。
参考:
如果 numrows 是一个数组,那很简单:
<tbody>
{numrows.map(item => <ObjectRow />)}
</tbody>
React 中的数组数据类型要好得多。一个数组可以支持一个新的数组,并支持过滤、减少等。
有几个答案指向使用 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 code。 features fixture is listed here。
假设我们在您的状态中有一系列项目:
[{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>
{this.state.items?.map((item) =>
,或者我最喜欢的语法坚定:{this.state.items && this.state.items.map((item) =>
并从 (item)
中删除不必要的 (),因为只有一个参数。你会因为异步渲染组件而没有导入本地状态而感到沮丧。
<tbody>
{
Array.from(Array(i)).map(() => <ObjectRow />)
}
</tbody>
其中i = number of times
如果您想将唯一键 ID 分配给呈现的组件,您可以使用 React documentation 中建议的 React.Children.toArray
React.Children.toArray
以平面数组的形式返回子级不透明数据结构,并为每个子级分配键。如果你想在你的渲染方法中操作孩子的集合,特别是如果你想在传递它之前重新排序或切片 this.props.children 时很有用。
注意: React.Children.toArray() 在展平子列表时更改键以保留嵌套数组的语义。也就是说, toArray 为返回数组中的每个键添加前缀,以便每个元素的键的范围都限定为包含它的输入数组。
<tbody>
{
React.Children.toArray(
Array.from(Array(i)).map(() => <ObjectRow />)
)
}
</tbody>
ECMAScript 2015 / Babel 的一种可能性是使用生成器函数来创建 JSX 数组:
function* jsxLoop(times, callback)
{
for(var i = 0; i < times; ++i)
yield callback(i);
}
...
<tbody>
{[...jsxLoop(numrows, i =>
<ObjectRow key={i}/>
)]}
</tbody>
这可以通过多种方式完成。
如上所述,在返回之前将所有元素存储在数组循环中返回方法1 let container = []; let arr = [1, 2, 3] //可以是任何数组,对象 arr.forEach((val, index) => { container.push(
...或者您也可以准备一个对象数组并将其映射到一个函数以获得所需的输出。我更喜欢这个,因为它可以帮助我保持良好的编码实践,而在渲染的返回中没有逻辑。
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>
)
}
ES2015 Array.from 带有 map 函数 + 键
如果您没有要 .map()
的内容,您可以将 Array.from()
与 map
函数一起使用来重复元素:
<tbody>
{Array.from({ length: 5 }, (value, key) => <ObjectRow key={key} />)}
</tbody>
我用这个:
gridItems = this.state.applications.map(app =>
<ApplicationItem key={app.Id} app={app } />
);
PS:永远不要忘记钥匙,否则你会收到很多警告!
.Id
属性(例如 items.map( (item, index) => <Foo key={index}>{item}</Foo>
),则使用数组索引
只需使用 .map()
循环您的集合并返回 <ObjectRow>
项以及每次迭代的道具。
假设 objects
是某处的数组...
<tbody>
{ objects.map((obj, index) => <ObjectRow obj={ obj } key={ index }/> ) }
</tbody>
您当然可以按照其他答案的建议使用 .map 解决。如果您已经使用 Babel,您可以考虑使用 jsx-control-statements。
它们需要一些设置,但我认为它在可读性方面是值得的(尤其是对于非 React 开发人员)。如果您使用 linter,还有 eslint-plugin-jsx-control-statements。
这是一个简单的解决方案。
var Object_rows = [];
for (var i = 0; i < numrows; i++) {
Object_rows.push(<ObjectRow />);
}
<tbody>{Object_rows}</tbody>;
不需要映射和复杂的代码。您只需要将行推送到数组并返回值以呈现它。
您的 JSX 代码将编译为纯 JavaScript 代码,任何标签都将被 ReactElement
对象替换。在 JavaScript 中,你不能多次调用一个函数来收集它们返回的变量。
这是非法的,唯一的办法就是使用数组来存储函数返回的变量。
或者您可以使用 since JavaScript ES5 可用的 Array.prototype.map
来处理这种情况。
也许我们可以编写其他编译器来重新创建一个新的 JSX 语法来实现一个重复函数,就像 Angular's ng-repeat
一样。
我倾向于采用一种方法,即编程逻辑发生在 render
的返回值之外。这有助于保持实际呈现的内容易于理解。
所以我可能会做类似的事情:
import _ from 'lodash';
...
const TableBody = ({ objects }) => {
const objectRows = objects.map(obj => <ObjectRow object={obj} />);
return <tbody>{objectRows}</tbody>;
}
诚然,这是如此少量的代码,内联它可能工作得很好。
你可以在 React for 循环中使用 .map() 。
<tbody>
{ newArray.map(() => <ObjectRow />) }
</tbody>
这是来自 React 文档 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 来区分数组中的数据。
由于您在 JSX 代码中编写 JavaScript 语法,因此您需要将 JavaScript 代码包装在花括号中。
row = () => {
var rows = [];
for (let i = 0; i<numrows; i++) {
rows.push(<ObjectRow/>);
}
return rows;
}
<tbody>
{this.row()}
</tbody>
我像这样使用它
<tbody>
{ numrows ? (
numrows.map(obj => { return <ObjectRow /> })
) : null
}
</tbody>
您还可以使用自调用函数:
return <tbody>
{(() => {
let row = []
for (var i = 0; i < numrows; i++) {
row.push(<ObjectRow key={i} />)
}
return row
})()}
</tbody>
如果您真的想要一个等效的 for 循环(您只有一个数字,而不是数组),只需使用 range
from Lodash。
不要重新发明轮子,也不要混淆你的代码。只需使用标准实用程序库。
import range from 'lodash/range'
range(4);
// => [0, 1, 2, 3]
range(1, 5);
// => [1, 2, 3, 4]
<tbody>{objects.map((o, i) => <ObjectRow obj={o} key={i}/>}</tbody>
使用 Reactify 的 ES6 支持或 Babel。return ( ... your lines here ... )