11

I have this problem. I want to loop an array and display the values as a table where each element is a row in the table. So far, no problems. But depending on a value in each element, I want to show an additional row with more values for each array element. I have this code:

<tbody>
  {myList.map((item, i) => {
    return (
      <div>
        <tr key={i} onClick={toggleMobileOpen.bind(this, i)}>
          <td className="toggler">
            {item.mobile_open && <ArrowUp />}
            {!item.mobile_open && <ArrowDown />}
          </td>
          <td>{item.elem_one}</td>
          <td>{item.elem_two}</td>
          <td>{item.elem_three}</td>
        </tr>
        {item.mobile_open &&
          <tr className="test-td">
            <td>...</td>
          </tr>
        }
      </div>
    );
  })}
</tbody>

My problem is that I get:

Warning: validateDOMNesting(...): Text nodes cannot appear as a child of <tr>.

and

Warning: validateDOMNesting(...): <div> cannot appear as a child of <tbody>

When I try the above approach. Removing the div gives me Syntax error which I already knew but tried anyway.

Does anyone have an idea how to solve this?

3 Answers 3

22

You're doing

{item.mobile_open &&
          <tr className="test-td">
            <td>...</td>
          </tr>
        }

that prints false like <tr>false</tr> if mobile_open is false.

try

{item.mobile_open ?
              (<tr className="test-td">
                <td>...</td>
              </tr>) : null
            }

As for the div warning consider using React 16 Fragments

using React 16.0 fragment syntax

<tbody>
  {myList.map((item, i) => {
    return [
        <tr key={i} onClick={toggleMobileOpen.bind(this, i)}>
          <td className="toggler">
            {item.mobile_open && <ArrowUp />}
            {!item.mobile_open && <ArrowDown />}
          </td>
          <td>{item.elem_one}</td>
          <td>{item.elem_two}</td>
          <td>{item.elem_three}</td>
        </tr>,
        {item.mobile_open &&
          <tr className="test-td">
            <td>...</td>
          </tr>
        }

    ];
  })}
</tbody>

But I prefer the most recent React 16.2 Fragment Syntax

import React, { Fragment } from "react";

<tbody>
      {myList.map((item, i) => {
        return (
          <Fragment>
            <tr key={i} onClick={toggleMobileOpen.bind(this, i)}>
              <td className="toggler">
                {item.mobile_open && <ArrowUp />}
                {!item.mobile_open && <ArrowDown />}
              </td>
              <td>{item.elem_one}</td>
              <td>{item.elem_two}</td>
              <td>{item.elem_three}</td>
            </tr>
            {item.mobile_open &&
              <tr className="test-td">
                <td>...</td>
              </tr>
            }
          </Fragment>
        );
      })}
    </tbody>

More on fragments here

Sign up to request clarification or add additional context in comments.

3 Comments

Perfect, tried Fragment and it worked fine. Just one thing, I needed to remove the comma after the first </tr>.
@BluePrint yep I'm gonna edit. Twas copy paste mistake
key attribute should be on Fragment @JoãoCunha
1

In React16 you are now able to return an array of components allowing you to remove the <div />.

The code would look something like :

 <tbody>
      {myList.map((item, i) => {
        return [
            <tr key={i} onClick={toggleMobileOpen.bind(this, i)}>
              <td className="toggler">
                {item.mobile_open && <ArrowUp />}
                {!item.mobile_open && <ArrowDown />}
              </td>
              <td>{item.elem_one}</td>
              <td>{item.elem_two}</td>
              <td>{item.elem_three}</td>
            </tr>,

            //This inline conditional makes it weird but this works
            ...[item.mobile_open &&
              <tr className="test-td">
                <td>...</td>
              </tr>
            ]
        ];
      })}
    </tbody>

Comments

0

This says it all: <div> cannot appear as a child of <tbody>

In myList.map(f), f should return exactly one <tr> each time it's called. So you could preprocess myList to contain JSON items than can then be rendered as you wish.

However, it's probably better to output one row for each data item you have, and display the additional information in a different way. Maybe insert a div inside the <td> when you click your arrow icon?

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.