繰り返し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テンプレートコード内にループを埋め込むか、同様のアプローチを使用したいと思います。)


2
このようなものが欲しいですか?stackoverflow.com/questions/22876978/loop-inside-react-jsx
patricK 2014

3
はい、それは私が望んでいることですが、受け入れられた答えは、私がすでに書いている醜いコードとほとんど同じです。...より良い方法があるに違いありません
fadedbee

私はこの意志のヘルプを願っていstackoverflow.com/a/37600679/1785635を
abhirathore2006

回答:


130

中括弧内に式を入れることができます。コンパイルされたJavaScriptで、forJSX構文内でループが発生しない理由に注意してください。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);
              })
            )
          );
        })
      )
    )
  );
} 

いいキャッチ。欠落している括弧を上部に追加し、からforに切り替えましたmap。各データ構造のデータがわからないため、行は配列であると想定し、の反復のインデックスを使用しましたkey。データが配列に追加/削除されると、不要な再レンダリングが発生します。keyデータを一意に識別し、配列の順序やサイズに依存しない値に切り替える必要があります。
ロス・アレン

@ssorallenご回答ありがとうございます。あなたの答えを受け入れる前に、これを行うより良い方法がある場合に備えて、私はこの質問を数日間開いたままにしておきます。私の問題は、JSXには、forforループなどの非式エスケープがないようであり、ループのテンプレート構文もないことだと思います。
fadedbee 2014

1
@chrisdew JSXはテンプレート言語ではなく、単純な古いJavaScriptの構文糖衣です。テンプレート言語に期待するような演算子は得られません。この回答のコードをライブJSXコンパイラに貼り付けて、それが何をしているのか、なぜforループが不可能になるのかを理解してください。
ロス・アレン

各行にonClickイベントを追加して、行アイテムを関数に渡すことはできますか?このようなことを試みていますが、Uncaught TypeErrorが発生しています:undefined clickHandler(){console.log( 'on click row');のプロパティ 'clickHandler'を読み取ることができません。} <tbody> {this.props.rows.map(function(row、i){return(<tr key = {i} onClick = this.clickHandler()> {row.map(function(col、j){return <td key = {j}> {col} </ td>;})} </ tr>);})} </ tbody>
キラン

mapメソッドに.bind(this)を追加したことを気にしないでください。そうすると、メソッドは各行に関与します。ありがとうございました。
キラン2017年

13

ロスアレンの答えを拡張するために、ES6矢印構文を使用した少しクリーンなバリアントを次に示します。

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

JSXパーツが分離されている(noreturnまたは;)という利点があり、ループを簡単に配置できます。


13

Array(3)反復不可能な配列を作成するため、mapArrayメソッドを使用できるように配列を設定する必要があります。「変換」する方法は、配列ブラケット内でそれを破棄することです。これにより、配列にundefined値が入力されるように「強制」されます。Array(N).fill(undefined)

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

別の方法は、配列を使用することfill()です。

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

上記の例で⚠️問題の欠如でkeyある小道具、必見
(イテレータの使用方法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時間を費やしました。ありがとう。あなたは私の日を救った。
シュエ

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%の確率でインデックスベースのキーで十分です。ややまれな例外については、少し邪魔にならないようにしたいと思います。
ブリガンド2014

同意しました。必要に応じて、特定のレベルでキーが一意であることを確認するのは簡単です。私はそれが好きです。
ライアン鉱石

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.