Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I'm creating a table in React (I'm new to React), but the CategoryDataCan we live with those or should we use something else? It is not creating cells correctly, i.e. it's creating cells that are not aligned with the <th> that come from the parent and they do not have cell borders at all. It's also giving these warnings:

Warning: validateDOMNesting(...): <div> cannot appear as a child of <tbody>. See Param > tbody > Row > div.
Warning: validateDOMNesting(...): <tr> cannot appear as a child of <div>. See Row > div > tr.
Warning: validateDOMNesting(...): <tr> cannot appear as a child of <div>. See CategoryData > div > tr.

I'm not sure why these warnings are happening and why the table cells(from CategoryData) are not getting aligned and don't have cell borders. What's the correct way to do it?

Code

var Param = React.createClass({

    getInitialState: function() {
        return {
            isLoading: true,
            panelTitle: "",
            data: [],
            categories: []
        }
    },

    updateState: function() {
        var that = this;
        var categories = new Set();
        rest.getParameters('service').then(function (results) {
            for (var i = 0; i < results.data.length; i++) {
                categories.add(results.data[i].category);
            }
            that.setState({
                data: results.data,
                categories: Array.from(categories)
            })
        }).catch(function (err) {
            console.error('rest.getParameters(): ', err);
            that.setState({
                isLoading: true,
                data: [],
                categories: []
            })
        });
    },

    componentDidMount: function() {
        this.updateState();
    },

    render: function() {
      return (
        <Panel className="panel-info" header={<h4 className="panel-title">Service Config</h4>}>
            <div>
                <table className="table table-striped table-bordered table-hover table-condensed table-responsive">
                    <thead>
                        <tr>
                            <th className="col-lg-2 text-center">AMP Name</th>
                            <th className="col-lg-2 text-center">Athena Name</th>
                            <th className="col-lg-2 text-center">Description</th>
                            <th className="col-lg-1 text-center">Default</th>
                            <th className="col-lg-1 text-center">Min Value</th>
                            <th className="col-lg-1 text-center">Max Value</th>
                            <th className="col-lg-1 text-center">Action</th>
                        </tr>
                    </thead>
                    <tbody>
                        {this.state.categories.map((category, index) =>
                            <th colSpan="7" key={index} style={{'textAlign':'left', 'paddingLeft':'5px', 'backgroundColor': '#D3D0CF'}}>{this.state.category}</th>
                            this.state.data.map((row, i) =>
                                if (row.category === category) {
                                    <tr key={i}>
                                        <td className="col-lg-2 text-center">{row.name}</td>
                                        <td className="col-lg-2 text-center">{row.alias}</td>
                                        <td className="col-lg-2 text-center">{row.description}</td>
                                        <td className="col-lg-1 text-center">{row.default_value}</td>
                                        <td className="col-lg-1 text-center">{row.min_value}</td>
                                        <td className="col-lg-1 text-center">{row.max_value}</td>
                                        <td className="col-lg-1 text-center">Action</td>
                                    </tr>
                                }
                            )  
                        )}
                    </tbody>
                </table>
            </div>
        </Panel>
    );
  }
});
like image 715
ssss Avatar asked Jul 26 '17 23:07

ssss


2 Answers

I would change the 'th' to a 'tr' because I'm pretty sure react will give you a warning if you add 'th' inside 'tbody'

let finalList = []
this.state.categories.forEach( (cat, index) => {
   finalList.push(<tr...>{this.state.category}</tr>)
   this.state.data.forEach( (row, index) => {
      if(row.category === cat){
         finalList.push(
             <tr key={i}>
                 <td className="col-lg-2 text-center">{row.name}</td>
                 <td className="col-lg-2 text-center">{row.alias}</td>
                 <td className="col-lg-2 text-center">{row.description}</td>
                 <td className="col-lg-1 text-center">{row.default_value}</td>
                 <td className="col-lg-1 text-center">{row.min_value}</td>
                 <td className="col-lg-1 text-center">{row.max_value}</td>
                 <td className="col-lg-1 text-center">Action</td>
             </tr>
          )
      }
   })
})

Word of warning I would avoid using tables checkout css grids their a lot more flexible and pretty well supported

like image 140
David Villarreal Avatar answered Nov 13 '22 17:11

David Villarreal


EDIT: From version 16.0.0 onwards in react, you could make use of React.Fragment to return multiple elements from render

<tbody>
    {
        this.state.categories.map((category, index) => {
            var innerData = this.state.data.map((row, i) => {
                if (row.category === category) {
                    return (
                        <tr key={i}>
                            <td className="col-lg-2 text-center">{row.name}</td>
                            <td className="col-lg-2 text-center">{row.alias}</td>
                            <td className="col-lg-2 text-center">{row.description}</td>
                            <td className="col-lg-1 text-center">{row.default_value}</td>
                            <td className="col-lg-1 text-center">{row.min_value}</td>
                            <td className="col-lg-1 text-center">{row.max_value}</td>
                            <td className="col-lg-1 text-center">Action</td>
                        </tr>
                    )
                }
                return null
            })

            return (
              <React.Fragment>
                <th colSpan="7" key={index} style={{
                    'textAlign': 'left',
                    'paddingLeft': '5px',
                    'backgroundColor': '#D3D0CF'
                }}>{this.state.category}</th>,
                {innerData}
              </React.Fragment>    
            )
        })
    }
    </tbody>

Before v16

With the help of JSX syntactic sugar it is possible to return multiple elements from within a component, by writing them as comma separated elements in an array like

<tbody>
{
    this.state.categories.map((category, index) => {
        var innerData = this.state.data.map((row, i) => {
            if (row.category === category) {
                return (
                    <tr key={i}>
                        <td className="col-lg-2 text-center">{row.name}</td>
                        <td className="col-lg-2 text-center">{row.alias}</td>
                        <td className="col-lg-2 text-center">{row.description}</td>
                        <td className="col-lg-1 text-center">{row.default_value}</td>
                        <td className="col-lg-1 text-center">{row.min_value}</td>
                        <td className="col-lg-1 text-center">{row.max_value}</td>
                        <td className="col-lg-1 text-center">Action</td>
                    </tr>
                )
            }
            return null
        })

        return ([
            <th colSpan="7" key={index} style={{
                'textAlign': 'left',
                'paddingLeft': '5px',
                'backgroundColor': '#D3D0CF'
            }}>{this.state.category}</th>,
            [...innerData]
        ])
    })
}
</tbody>

Also when you make use of if statements within a map function, you need to have them outside of the return statement, now if you do {this.state.categories.map((category, index) => <tr>... it means that whatever is after the arrow is considered to be part of the return and hence you inner map's if statement will give you an error.

There is an issue on react github page for returning multiple elements. Read through it for more details.

like image 4
Shubham Khatri Avatar answered Nov 13 '22 16:11

Shubham Khatri