Как обернуть компонент React, который возвращает несколько строк таблицы, и избежать появления ошибки «<tr> не может быть потомком <div>»?

У меня есть компонент с именем OrderItem, который принимает объект с несколькими объектами (по крайней мере, двумя) внутри него и отображает их как несколько строк внутри таблицы. Внутри таблицы будет несколько элементов OrderItem com, ponents. Проблема в том, что в функции рендеринга компонента я не могу вернуть несколько строк. Я могу вернуть только один компонент, и если я заверну их в div, он говорит:<tr> не может появиться как ребенок<div>"

Код выглядит примерно так (я упустил некоторые вещи для удобства чтения)

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

class OrderItem extends React.Component {
  render() {
    return (
      <div> // <-- problematic div
        <tr key={this.props.myKey}>
          <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>
            )
          }
        })}
      </div>
    )
  }
}

Есть ли способ, которым я могу вернуть эти несколько строк, и чтобы они были в одной таблице, не заключая их в div и не получая ошибку? Я понимаю, что могу создать отдельную таблицу для каждого компонента, но это немного отбрасывает мое форматирование.

 Pedram06 окт. 2016 г., 16:08
Это похоже на решение, приведенное ниже, с несколькими табличными тегами, только с tbody вместо table.
 magritte21 июн. 2018 г., 10:57
Я считаю, что это возможно сейчас с использованиемreactjs.org/docs/fragments.html
 Dom Hede06 окт. 2016 г., 01:23
Одним из вариантов может быть удаление тега tbody верхнего уровня. Затем позвольте карте вернуться в теге <tbody>.

Ответы на вопрос(5)

ицы. Решение, которое не выдает консольных ошибок и семантически правильно на самом деле, заключается в использованииtbody элемент в качестве корневого компонента и заполните столько строк, сколько требуется.

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

Следующий пост посвящен этическим вопросам и объясняет, почемуда мы можем использовать несколькоtbody элементыМожем ли мы иметь несколько <tbody> в одной и той же <таблице>?

 trevorgk26 июн. 2018 г., 03:08
Для записи это казалось хорошей идеей в то время, но в реакции 16 мы можем и должны использовать фрагменты вместо этого.

но, возможно, кто-то на него споткнется. Поскольку я пока не могу комментировать, вот небольшое дополнение к ответу @trevorgk:

Я использовал это для рендеринга таблицы с несколькими строками на элемент (около 1000 элементов, в результате чего получается около 2000 строк с 15 столбцами) и заметил очень низкую производительность с Firefox (даже в 57).

У меня были чистые компоненты, отображающие каждый элемент (по одному <body> на элемент, содержащий по две строки в каждой), и каждый элемент содержал (контролируемый) флажок.

При установке флажка Firefox потребовалось более десяти секунд для обновления, хотя фактически только один элемент был обновлен из-за чистых компонентов. Обновление Chrome заняло не более полсекунды.

Я переключился на React 16 и не заметил никакой разницы. Тогда я использовал новый УДИВИТЕЛЬНЫЙ !!! особенность возврата массива из функции рендеринга компонента и избавления от 1000 элементов <tbody>. Производительность Chrome была примерно такой же, в то время как Firefox «взлетел» до полсекунды для обновления (нет заметной разницы с Chrome)

Решение Вопроса

что нет способа аккуратно их обернуть, поэтому более простое решение - просто поместить всю таблицу в компонент и просто иметь несколько таблиц и выяснить форматирование.

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

React 16 сейчас здесь, чтобы спасти, теперь вы можете использоватьReact.Fragment отображать список элементов без переноса его в родительский элемент. Вы можете сделать что-то вроде этого:

  return (
    <React.Fragment>
      <tr>
        ...
      </tr>
    </React.Fragment>
  );
}
 Luigi26 июн. 2019 г., 14:17
Фрагменты - React doc:reactjs.org/docs/fragments.html
 Siddhartha04 июн. 2019 г., 21:19
<React.Fragment> кажется, работает намного лучше, чем<tbody> тоже из моего опыта.

чтобы разделитьOrderItem на две составляющие, перемещая логику рендеринга в метод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>
    )
  }
}
 Pedram24 июн. 2016 г., 22:43
Спасибо за ответ. Кажется, что было бы немного сложнее просто поместить таблицу в один компонент.

Ваш ответ на вопрос