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

了解如何将函数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 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 的工作方式。

不知道这是否适合您的情况,但通常使用地图是个不错的选择。

如果这是带有 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 进行转译时,其警告页面说,传播需要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()解决方案中纠正了我的错误(请参阅修订)。

key

在所有情况下, key属性都会减轻开发版本的警告,但在子级中无法访问。如果希望子级中的索引可用,则可以传递一个额外的 attr。请参阅列表和键进行讨论。

只需使用具有 ES6 语法的map Array方法即可:

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

不要忘记key属性。

使用数组映射功能是在React 中循环遍历元素数组并根据其创建组件的一种非常常见的方法,这是执行循环的一种好方法,这是在JSX 中进行循环的非常有效和整洁的方法,这不是唯一的方法的方法,但首选的方法。

另外,不要忘记根据需要为每个迭代都拥有唯一的 KeyMap 函数从 0 开始创建一个唯一索引,但是不建议使用产生的索引,但是如果您的值是唯一的或存在唯一键,则可以使用它们:

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

另外,如果您不熟悉 Array 上的 map 函数,则从MDN出发几行:

map 依次为数组中的每个元素调用提供的回调函数,然后从结果中构造一个新的数组。仅对具有已分配值(包括未定义)的数组索引进行调用。不会为缺少数组的元素(即从未设置,已删除或从未分配值的索引)调用它。

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

如果提供 thisArg 参数来映射,它将用作回调的 this 值。否则,未定义的值将用作此值。回调最终可观察到的该值是根据确定函数所看到的值的常规规则确定的。

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}>
                            <Select onSelect={this.onSelect}>
                                <option value="1">bacon</option>
                                <option value="2">cheez</option>
                            </Select>
                        </li>
                    )}
                </ol>
            </div>
        );
    }
}

您还可以提取 return 块之外的内容:

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

    return (<tbody>{rows}</tbody>);
}

我知道这是一个旧线程,但是您可能想签出React 模板 ,它确实允许您在React 中使用 jsx 样式的模板,并带有一些指令(例如 rt-repeat)。

如果使用 react-templates,那么您的示例将是:

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

有多种方法可以做到这一点。 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><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>
  );
}

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

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

React 中的数组数据类型非常好,数组可以支持新数组,并支持过滤,缩小等。