score:4

Accepted answer

It seems there is no way to wrap them cleanly, so the easier solution is to just put the whole table in the component and just have multiple tables and figure out the formatting.

Parent() {
   render() {
       return (
           {_.map(this.state.orderItems, (value, key) => {
               return <OrderItem value={value} myKey={key} key={key}/>
           })}
       )
   }
}


class OrderItem extends React.Component {
    render() {
        return (
            <table>
                <tbody>
                   <tr>
                       <td> Table {this.props.value[0].table}</td>
                       <td> Item </td>
                       <td> Option </td>
                    </tr>
                    {this.props.value.map((item, index) => {
                        if (index > 0) { // skip the first element since it's already used above
                            return (
                                <tr key={this.props.myKey + index.toString()}>
                                    <td> <img src={item.image} alt={item.name} width="50"/> {item.name}</td>  
                                    <td>{item.selectedOption}</td>
                                </tr>
                            )
                        }
                    })}
                </tbody>
            </table>
        )
    }
}

score:1

In my case, the solution was to return an array instead of a fragment:

    return [
        <TrHeader />,
        <TrRows />
    ];

score:4

It is an old question, but maybe someone stumbles on it. Since I cannot comment yet, here is a little addition to the answer of @trevorgk:

I used this to render a table with multiple rows per item (about 1000 items resulting in about 2000 rows with 15 columns) and noticed really bad performance with Firefox (even in 57).

I had pure components rendering each item (one <body> per item containing two rows each) and each item contained a (controlled) checkbox.

When clicking the checkbox Firefox took more than ten seconds to update - although only one item was actually updated due to pure components. Chrome's update took at most half a second.

I switched to React 16 and I noticed no difference. Then I used the new AWESOME!!! feature of returning an array from a component's render function and got rid of the 1000 <tbody> elements. Chrome's performance was approximately the same while Firefox's "skyrocketed" to about half a second for an update (no perceived difference to Chrome)

score:14

One approach is to split OrderItem into two components, moving the rendering logic into a method Parent.renderOrderItems:

class Parent extends React.Component {
  renderOrderItems() {
    const rows = []
    for (let orderItem of this.state.orderItems) {
      const values = orderItem.value.slice(0)
      const headerValue = values.shift()
      rows.push(
        <OrderItemHeaderRow table={headerValue.table} key={orderItem.key} />
      )
      values.forEach((item, index) => {
        rows.push(
          <OrderItemRow item={item} key={orderItem.key + index.toString()} />
        )
      })
    }
    return rows
  }
  render() {
    return (
      <table>
        <tbody>
          { this.renderOrderItems() }
        </tbody>
      </table>
    )
  }
}

class OrderItemHeaderRow extends React.Component {
  render() {
    return (
      <tr>
        <td> Table {this.props.table}</td>
        <td> Item </td>
        <td> Option </td>
      </tr>
    )
  }
}

class OrderItemRow extends React.Component {
  render() {
    const { item } = this.props
    return (
      <tr>
        <td>
          <img src={item.image} alt={item.name} width="50"/>
          {item.name}
        </td>
        <td>
          {item.selectedOption}
        </td>
      </tr>
    )
  }
}

score:30

Yes!! It is possible to map items to multiple table rows inside a table. A solution which doesn't throw console errors and semantically is actually correct, is to use a tbody element as the root component and fill with as many rows as required.

items.map(item => (
   <tbody>
       <tr>...</tr>
       <tr>...</tr>
   </tbody>
))

The following post deals with the ethical questions about it and explains why yes we can use multiple tbody elements Can we have multiple <tbody> in same <table>?

score:36

React 16 is now here to rescue, you can now use React.Fragment to render list of elements without wrapping it into a parent element. You can do something like this:

render() {
  return (
    <React.Fragment>
      <tr>
        ...
      </tr>
    </React.Fragment>
  );
}

Related Query

More Query from same tag