Подтвердить что ты не робот

Как обернуть компонент React, который возвращает несколько строк таблицы, и избежать "<tr> не может появиться в виде дочернего элемента <div>"?

У меня есть компонент OrderItem, который принимает объект с несколькими объектами (по крайней мере два) внутри него и отображает их как несколько строк внутри таблицы. В таблице будет несколько компонентов OrderItem. Проблема в том, что в функции рендеринга компонента я не могу вернуть несколько строк. Я могу только вернуть один компонент, и если я оберну их в 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 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 и получая ошибку? Я понимаю, что могу создать отдельную таблицу для каждого компонента, но это немного меняет форматирование.

4b9b3361

Ответ 1

Кажется, что нет никакого способа обернуть их чисто, поэтому проще всего просто поместить всю таблицу в компонент и просто иметь несколько таблиц и определить форматирование.

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

Ответ 2

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

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

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

Ответ 3

Один из подходов состоит в разделении 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>
    )
  }
}

Ответ 4

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

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

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

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

Я переключился на React 16, и я не заметил никакой разницы. Затем я использовал новый AWESOME!!! функция возврата массива из функции визуализации компонента и избавилась от 1000 <tbody> элементы. Производительность Chrome была примерно такой же, в то время как Firefox "взлетел" примерно до половины секунды для обновления (без видимой разницы с Chrome).