如何包装返回多个表行的 React 组件并避免“<tr> 不能作为 <div> 的子项出现”错误?

IT技术 javascript reactjs
2021-04-16 01:11:34

我有一个名为 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'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 中并出现错误?我意识到我可以为每个组件制作一个单独的表格,但这会使我的格式有点偏离。

6个回答

React 16现在来拯救,你现在可以React.Fragment用来渲染元素列表,而无需将其包装到父元素中。你可以这样做:

render() {
  return (
    <React.Fragment>
      <tr>
        ...
      </tr>
    </React.Fragment>
  );
}
<React.Fragment><tbody>从我的经验来看,似乎比太多工作要好得多
2021-06-10 01:11:34

是的!!可以将项目映射到表内的多个表行。一个不会抛出控制台错误并且语义上实际上是正确的解决方案是使用tbody元素作为根组件并根据需要填充尽可能多的行。

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

下面的帖子处理了关于它的道德问题,并解释了为什么是的,我们可以使用多个tbody元素 我们可以在同一个 <table> 中有多个 <tbody> 吗?

根据记录,这在当时似乎是一个好主意,但在 react 16 中,我们可以而且应该使用片段代替。
2021-06-16 01:11:34

一种方法是拆分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>
    )
  }
}
谢谢你的回答。不过,将表格放在一个组件中似乎会简单一些。
2021-06-21 01:11:34

似乎没有办法将它们干净地包装起来,因此更简单的解决方案是将整个表格放在组件中,并且只有多个表格并弄清楚格式。

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

这是一个古老的问题,但也许有人偶然发现了它。由于我还不能发表评论,这里是对@trevorgk 的回答的补充:

我用它来渲染一个每个项目有多行的表格(大约 1000 个项目导致大约 2000 行和 15 列)并注意到 Firefox 的性能非常差(即使在 57 中)。

我有纯组件呈现每个项目(每个项目一个 <body>,每个项目包含两行),每个项目包含一个(受控)复选框。

当单击复选框时,Firefox 花了十多秒钟来更新 - 尽管由于纯组件,实际上只更新了一项。Chrome 的更新最多只用了半秒。

我切换到 React 16,我发现没有区别。然后我用了新的AWESOME!!!从组件的渲染函数返回数组的功能,并摆脱了 1000 个 <tbody> 元素。Chrome 的性能大致相同,而 Firefox 的更新“猛增”到大约半秒(与 Chrome 没有明显差异)