如何包装一个返回多个表行的React组件,并避免“不能显示为的子项”错误?[英]How do I wrap a React component that returns multiple table rows and avoid the “ cannot appear as a child of ” error? 本文翻译自  Pedram  查看原文  2016-06-23  16370   

时间:2022-08-22 21:17:22

I have a component called OrderItem that takes an object with multiple objects (at least two) inside it, and renders them as multiple rows inside a table. There will be multiple OrderItem components inside the table. The problem is that in the component's render function, I can't return multiple lines. I can only return a single component, and if I wrap them in a div, it says " <tr> cannot appear as a child of <div>"

我有一个名为OrderItem的组件,它接受一个包含多个对象(至少两个)的对象,并将它们呈现为表中的多个行。表中会有多个OrderItem组件。问题是在组件的渲染功能中,我不能返回多行。我只能返回一个组件,如果我将它们包装在div中,它会说“不能作为

的子项出现”

The code looks something like this (I left some stuff out for easier readability)

代码看起来像这样(为了更容易阅读,我留下了一些东西)

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

Is there a way I can return those multiple rows and have them be in the same table without wrapping them in a div and getting an error? I realize I can make a separate table for each component, but that throws my formatting off a bit.

有没有办法可以返回那些多行并让它们在同一个表中而不将它们包装在div中并得到错误?我意识到我可以为每个组件创建一个单独的表,但这会使我的格式稍微抛出。

5 个解决方案

#1


26  

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.

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

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>?

以下文章介绍了有关它的道德问题,并解释了为什么我们可以使用多个tbody元素我们可以在同一个

中有多个 吗?

#2


13  

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

一种方法是将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>
    )
  }
}

#3


4  

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

#4


3  

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:

这是一个老问题,但也许有人偶然发现它。由于我还没有发表评论,这里有一点@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).

我使用它来渲染一个每个项目有多行的表(大约1000个项目,导致大约2000行,15列),并注意到Firefox的性能非常糟糕(即使在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.

单击复选框时,Firefox需要十几秒钟才能更新 - 尽管由于纯组件,实际上只有一个项目已更新。 Chrome的更新最多需要半秒钟。

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)

我切换到React 16,我注意到没有区别。然后我用了新的真棒!从组件的渲染函数返回数组并删除1000 元素的功能。 Chrome的性能大致相同,而Firefox的“暴涨”大约为半秒钟的更新(与Chrome无差异)

#5


0  

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:

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

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

#1


26  

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.

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

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>?

以下文章介绍了有关它的道德问题,并解释了为什么我们可以使用多个tbody元素我们可以在同一个

中有多个 吗?

#2


13  

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

一种方法是将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>
    )
  }
}

#3


4  

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

#4


3  

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:

这是一个老问题,但也许有人偶然发现它。由于我还没有发表评论,这里有一点@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).

我使用它来渲染一个每个项目有多行的表(大约1000个项目,导致大约2000行,15列),并注意到Firefox的性能非常糟糕(即使在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.

单击复选框时,Firefox需要十几秒钟才能更新 - 尽管由于纯组件,实际上只有一个项目已更新。 Chrome的更新最多需要半秒钟。

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)

我切换到React 16,我注意到没有区别。然后我用了新的真棒!从组件的渲染函数返回数组并删除1000 元素的功能。 Chrome的性能大致相同,而Firefox的“暴涨”大约为半秒钟的更新(与Chrome无差异)

#5


0  

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:

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

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