如何渲染重复的React元素?


81

我已经写了一些代码来渲染ReactJS中的重复元素,但是我讨厌它是如此丑陋。

render: function(){
  var titles = this.props.titles.map(function(title) {
    return <th>{title}</th>;
  });
  var rows = this.props.rows.map(function(row) {
    var cells = [];
    for (var i in row) {
      cells.push(<td>{row[i]}</td>);
    }
    return <tr>{cells}</tr>;
  });
  return (
    <table className="MyClassName">
      <thead>
        <tr>{titles}</tr>
      </thead>
      <tbody>{rows}</tbody>
    </table>
  );
} 

有没有更好的方法来实现这一目标?

(我想for在模板代码或类似方法中嵌入循环。)



3
是的,这就是我想要的,但是可接受的答案与我已经编写的丑陋代码几乎相同。必须有更好的方法……
fadebee 2014年

Answers:


130

您可以将表达式放在大括号内。请注意,在已编译的JavaScript中,为什么for在JSX语法中永远不可能实现循环;JSX相当于函数调用和加糖的函数参数。只允许使用表达式。

(此外:请记住将key属性添加到在循环内渲染的组件。)

JSX + ES2015

render() {
  return (
    <table className="MyClassName">
      <thead>
        <tr>
          {this.props.titles.map(title =>
            <th key={title}>{title}</th>
          )}
        </tr>
      </thead>
      <tbody>
        {this.props.rows.map((row, i) =>
          <tr key={i}>
            {row.map((col, j) =>
              <td key={j}>{col}</td>
            )}
          </tr>
        )}
      </tbody>
    </table>
  );
} 

JavaScript

render: function() {
  return (
    React.DOM.table({className: "MyClassName"}, 
      React.DOM.thead(null, 
        React.DOM.tr(null, 
          this.props.titles.map(function(title) {
            return React.DOM.th({key: title}, title);
          })
        )
      ), 
      React.DOM.tbody(null, 
        this.props.rows.map(function(row, i) {
          return (
            React.DOM.tr({key: i}, 
              row.map(function(col, j) {
                return React.DOM.td({key: j}, col);
              })
            )
          );
        })
      )
    )
  );
} 

不错的收获。我在顶部添加了缺少的括号,并从切换formap。由于我不知道每个数据结构中的数据是什么,因此我假设行是数组,并使用的迭代索引作为key。从数组中添加/删除数据时,将导致不必要的重新渲染。您应该切换key到唯一标识数据的值,并且该值不依赖于数组的顺序和/或大小。
罗斯·艾伦

@ssorallen感谢您的回答。如果可以采用一种更好的方法,在接受您的回答之前,我将把这个问题搁置几天。我认为我的问题是,JSX似乎没有用于for循环等的非表达式转义,也没有用于循环的模板语法。
褪色的蜜蜂2014年

1
@chrisdew JSX不是模板语言,它是普通旧JavaScript的语法糖。您不会从模板语言中获得期望的运算符。尝试将此答案中的代码粘贴到实时JSX编译器中,以了解其功能以及为什么for永远无法进行循环。
罗斯·艾伦

您可以为每行添加onClick事件并将行项目传递给函数吗?我正在尝试类似的操作,但是我遇到了Uncaught TypeError:无法读取未定义clickHandler()的属性'clickHandler'{console.log('单击行'); } <tbody> {this.props.rows.map(function(row,i){返回(<tr key = {i} onClick = this.clickHandler()> {row.map(function(col,j){ <td key = {j}> {col} </ td>;})} </ tr>);})} </ tbody>
Kiran 2017年

没关系,我已经将.bind(this)添加到map方法中,然后该方法将涉及到每一行。谢谢。
柯兰(Kiran)

13

为了扩展Ross Allen的答案,这是使用ES6箭头语法的更简洁的变体。

{this.props.titles.map(title =>
  <th key={title}>{title}</th>
)}

这样做的好处是JSX部分是隔离的(否return;),从而使围绕它的循环更容易。


13

由于Array(3)将创建一个不可迭代的数组,因此必须填充该map数组以允许使用Array方法。“转换”的一种方法是在Array括号内销毁它,这“迫使” Array填充undefined值,与Array(N).fill(undefined)

<table>
    { [...Array(3)].map((_, index) => <tr key={index}/>) }
</table>

另一种方法是通过Array fill()

<table>
    { Array(3).fill(<tr/>) }
</table>

上述示例的问题是缺少key道具,这是必须的
(使用iterator的index作为key,不推荐)


嵌套节点:

const tableSize = [3,4]
const Table = (
    <table>
        <tbody>
        { [...Array(tableSize[0])].map((tr, trIdx) => 
            <tr key={trIdx}> 
              { [...Array(tableSize[1])].map((a, tdIdx, arr) => 
                  <td key={trIdx + tdIdx}>
                  {arr.length * trIdx + tdIdx + 1}
                  </td>
               )}
            </tr>
        )}
        </tbody>
    </table>
);

ReactDOM.render(Table, document.querySelector('main'))
td{ border:1px solid silver; padding:1em; }
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<main></main>


2
我花了至少2个小时才能在render函数中循环。谢谢。你救了我的一天。

2
您不能这样做而不是使用地图吗?Array.from({length: 5}, (value, index) => <tr />)
creativehandle

2

本着函数式编程的精神,让我们通过使用抽象使我们的组件更易于使用。

// converts components into mappable functions
var mappable = function(component){
  return function(x, i){
    return component({key: i}, x);
  }
}

// maps on 2-dimensional arrays
var map2d = function(m1, m2, xss){
  return xss.map(function(xs, i, arr){
    return m1(xs.map(m2), i, arr);
  });
}

var td = mappable(React.DOM.td);
var tr = mappable(React.DOM.tr);
var th = mappable(React.DOM.th);

现在我们可以这样定义渲染了:

render: function(){
  return (
    <table>
      <thead>{this.props.titles.map(th)}</thead>
      <tbody>{map2d(tr, td, this.props.rows)}</tbody>
    </table>
  );
}

jsbin


我们的map2d的替代方法是咖喱地图功能,但人们倾向于避开咖喱。


这些功能不会给开发人员提供指定key属性的机会。使用索引意味着添加/删除元素将强制重新渲染可能未更改的对象。
罗斯·艾伦

正确,但是95%的时间基于索引的键就足够了。在某些罕见的例外情况下,我宁愿略过。
Brigand'9

同意,如果需要的话,很容易确保密钥在任何给定级别都是唯一的,我喜欢。
瑞安·奥瑞

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.