Làm cách nào để hiển thị các phần tử React lặp lại?


81

Tôi đã viết một số mã để hiển thị các phần tử lặp lại trong ReactJS, nhưng tôi ghét nó xấu xí đến mức nào.

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>
  );
} 

Có cách nào tốt hơn để đạt được điều này không?

(Tôi muốn nhúng forcác vòng trong mã mẫu hoặc một số cách tiếp cận tương tự.)


2
Bạn có muốn một cái gì đó như thế này? stackoverflow.com/questions/22876978/loop-inside-react-jsx
patricK

3
Vâng, đó là những gì tôi muốn, nhưng câu trả lời được chấp nhận rất giống với đoạn mã xấu xí mà tôi đang viết. Có phải là một cách tốt hơn ...
fadedbee

tôi hy vọng điều này sẽ giúp stackoverflow.com/a/37600679/1785635
abhirathore2006

Câu trả lời:


130

Bạn có thể đặt các biểu thức bên trong dấu ngoặc nhọn. Lưu ý trong JavaScript đã biên dịch tại sao một forvòng lặp sẽ không bao giờ có thể thực hiện được bên trong cú pháp JSX; JSX tương ứng với các lệnh gọi hàm và các đối số hàm có đường. Chỉ cho phép các biểu thức.

(Ngoài ra: Hãy nhớ thêm keythuộc tính vào các thành phần được hiển thị bên trong các vòng lặp.)

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);
              })
            )
          );
        })
      )
    )
  );
} 

Bắt đẹp. Tôi đã thêm dấu ngoặc đơn bị thiếu ở trên cùng và chuyển từ forthành dấu a map. Vì tôi không biết dữ liệu là gì trong mỗi cấu trúc dữ liệu, tôi đã giả định các hàng là mảng và sử dụng chỉ mục của lần lặp cho key. Khi dữ liệu được thêm vào / xóa khỏi mảng, điều đó sẽ gây ra kết xuất không cần thiết. Bạn nên chuyển keysang một giá trị xác định duy nhất dữ liệu và không phụ thuộc vào thứ tự và / hoặc kích thước của mảng.
Ross Allen

@ssorallen Cảm ơn câu trả lời của bạn. Tôi sẽ để ngỏ câu hỏi này trong vài ngày, trong trường hợp có cách tốt hơn để làm điều này, trước khi chấp nhận câu trả lời của bạn. Tôi nghĩ rằng vấn đề của tôi là JSX dường như không có lối thoát không biểu thức, cho forcác vòng lặp và tương tự, cũng như không có cú pháp mẫu cho các vòng lặp.
fadebee

1
@chrisdew JSX không phải là một ngôn ngữ mẫu, nó là một đường cú pháp cho JavaScript cũ thuần túy. Bạn sẽ không nhận được các toán tử như bạn mong đợi từ các ngôn ngữ mẫu. Hãy thử dán mã trong câu trả lời này vào trình biên dịch JSX trực tiếp để hiểu nó đang làm gì và tại sao forkhông bao giờ có thể thực hiện được vòng lặp.
Ross Allen

Bạn có thể thêm sự kiện onClick cho mỗi hàng và chuyển mục hàng vào hàm không? Tôi đang thử một cái gì đó như thế này nhưng tôi nhận được Uncaught TypeError: Không thể đọc thuộc tính 'clickHandler' của clickHandler không xác định () {console.log ('trên hàng nhấp chuột'); } <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>
Kiran

Đừng bận tâm, tôi đã thêm .bind (this) vào phương thức bản đồ thì phương thức này sẽ tham gia vào từng hàng. Cảm ơn bạn.
Kiran

13

Để mở rộng câu trả lời của Ross Allen, đây là một biến thể gọn gàng hơn một chút sử dụng cú pháp mũi tên ES6.

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

Nó có lợi thế là phần JSX được tách biệt (không returnhoặc ;), giúp dễ dàng đặt vòng lặp xung quanh nó.


13

Array(3)sẽ tạo ra một mảng không thể lặp lại , nó phải được điền để cho phép sử dụng mapphương thức Mảng. Một cách để "chuyển đổi" là hủy nó bên trong dấu ngoặc nhọn Mảng, điều này "buộc" Mảng phải được lấp đầy bằng undefinedcác giá trị, giống nhưArray(N).fill(undefined)

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

Một cách khác sẽ là thông qua Array fill():

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

Vấn đề với ví dụ trên là thiếu giá keyđỡ, đó là điều bắt buộc .
(Sử dụng một iterator ấy indexnhư keykhông được khuyến khích)


Các nút lồng nhau:

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
Tôi đã dành ít nhất 2 giờ để có thể lặp lại bên trong vòng lặp trong chức năng kết xuất. cảm ơn. bạn đã cứu ngày của tôi.
Shwe

2
Bạn không thể làm điều này thay vì sử dụng bản đồ? Array.from({length: 5}, (value, index) => <tr />)
creativehandle

2

Theo tinh thần của lập trình chức năng, hãy làm cho các thành phần của chúng ta dễ làm việc hơn một chút bằng cách sử dụng các trừu tượng.

// 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);

Bây giờ chúng ta có thể xác định kết xuất của mình như thế này:

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

jsbin


Một thay thế cho map2d của chúng tôi sẽ là một chức năng bản đồ cà ri, nhưng mọi người có xu hướng né tránh việc làm cà ri.


Các hàm đó không cho nhà phát triển cơ hội để chỉ định keythuộc tính. Sử dụng chỉ mục có nghĩa là việc thêm / xóa các phần tử sẽ buộc hiển thị lại các đối tượng có thể không thay đổi.
Ross Allen

Đúng, nhưng 95% thời gian các khóa dựa trên chỉ mục là đủ. Tôi thà đi xa một chút về những ngoại lệ hơi hiếm.
Brigand

Đồng ý, Sẽ rất dễ dàng để đảm bảo rằng các khóa là duy nhất ở bất kỳ cấp độ nhất định nào nếu cần thiết, tôi thích nó.
Ryan Ore

2

Đây là, imo, cách tốt nhất để làm điều đó (với ES6). Tạo cho bạn mảng trống với 7 chỉ mục và ánh xạ trong một dòng:

Array.apply(null, Array(7)).map((i)=>
<Somecomponent/>
)

kudos tới https://php.quicoto.com/create-loop-inside-react-jsx/


1
Đẹp! Dường như bạn cũng có thể làmArray(...Array(10)).map((v, i) => <SomeComponent />)
Magritte
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.