2

I'm getting an error when I'm rendering my react components and I think it has to with how my table are nested from component to component.

I'm getting this error Text nodes cannot appear as a child of <tbody>

I'm looking at answers here and here but I cannot see what I'm doing wrong with my nesting of the tables, to get this error.

My nesting for my tables is:

<table>
  <thead>
    <tr></tr>
  </thead>
  <tbody>
    <tr>
      <td></td>
    </tr>
  </tbody>
</table>

Here is my code:

import React, {Component} from 'react';
import './App.css';

class DataTable extends Component {
    constructor(props) {
        super(props);
        this.state = {
            dataBaseJSON: {},
            dataBaseItems: []
        };
    }

    componentDidMount() {
        fetch('https://ghibliapi.herokuapp.com/films').then(results => {
            return results.json();

        }).then(jsonResults => {
            this.setState({
                dataBaseJSON: jsonResults
            });
            return jsonResults

        }).then(jsonResults => {
            Object.keys(jsonResults).forEach(movieObject => {
                this.setState({
                    dataBaseItems: this.state.dataBaseItems.push(<DataRow key={this.state.dataBaseJSON[movieObject].id}
                                                                          item={this.state.dataBaseJSON[movieObject]}/>)
                })
            });
        })
    }

    render() {

        return (
            <table>
                <thead>
                <tr>
                    <th>Title</th>
                    <th>Director</th>
                    <th>Producer</th>
                    <th>Release Date</th>
                    <th>Rotten Tomato Score</th>
                </tr>
                </thead>
                <tbody>
                {this.state.dataBaseItems}
                </tbody>
            </table>

        )
    }
}

class DataRow extends Component {
    render() {
        return (
            <tr>
                <td>{this.props.item.title}</td>
                <td>{this.props.item.director}</td>
                <td>{this.props.item.producer}</td>
                <td>{this.props.item.release_date}</td>
                <td>{this.props.item.rt_score}</td>
            </tr>
        )
    }
}

class App extends Component {
    render() {
        return (
            <div>
                <h1 className="App-title">Welcome to React</h1>
                <div>
                    <DataTable/>
                </div>
            </div>

        );
    }
}

export default App;
Chef1075
  • 2,614
  • 9
  • 40
  • 57

2 Answers2

2

class DataTable extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            dataBaseJSON: {},
            dataBaseItems: []
        };
    }

    componentDidMount() {
      fetch('https://ghibliapi.herokuapp.com/films').then(results => {
            return results.json();

        }).then(jsonResults => {
        debugger
            this.setState({
                dataBaseJSON: jsonResults
            });
            return jsonResults

        }).then(jsonResults => {
            Object.keys(jsonResults).forEach(movieObject => {
                this.setState({
                    dataBaseItems: [
                    ...this.state.dataBaseItems,
                    <DataRow key={this.state.dataBaseJSON[movieObject].id}
                                                                          item={this.state.dataBaseJSON[movieObject]}/>
                    ]
                })
            });
        })
    }

    render() {

        return (
            <table>
                <thead>
                <tr>
                    <th>Title</th>
                    <th>Director</th>
                    <th>Producer</th>
                    <th>Release Date</th>
                    <th>Rotten Tomato Score</th>
                </tr>
                </thead>
                <tbody>
                {this.state.dataBaseItems}
                </tbody>
            </table>

        )
    }
}

class DataRow extends React.Component {
    render() {
        return (
            <tr>
                <td>{this.props.item.title}</td>
                <td>{this.props.item.director}</td>
                <td>{this.props.item.producer}</td>
                <td>{this.props.item.release_date}</td>
                <td>{this.props.item.rt_score}</td>
            </tr>
        )
    }
}

class App extends React.Component {
    render() {
        return (
            <div>
                <h1 className="App-title">Welcome to React</h1>
                <div>
                    <DataTable/>
                </div>
            </div>

        );
    }
}

ReactDOM.render(<App />, document.getElementById("root"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="root"></div>
So basically, What you were doing wrong was updating the same array in consecutive setstate calls. Rerendering happens when there is state change.state change means change of reference. in case of objects and arrays, new object/array has to be assigned every time so that reconciler knows to rerender. This may help
simbathesailor
  • 3,681
  • 2
  • 19
  • 30
  • Thanks! I found an explanation of the "..." used in the setState here [here](https://stackoverflow.com/questions/31048953/what-do-these-three-dots-in-react-do) – Chef1075 Jan 05 '18 at 04:07
  • Code-only answers aren't helpful. You should explain why the OP has their issue and how your code fixes it. – RobG Jan 05 '18 at 04:13
1

Your push will mutate the state directly and that could potentially lead to error-prone code.

this.state.dataBaseItems.push(<DataRow key={this.state.dataBaseJSON[movieObject].id}
                                       item={this.state.dataBaseJSON[movieObject]}/>)

You can use concat to get a cleaner syntax since it returns a new array.

 this.setState({
          dataBaseItems: this.state.dataBaseItems.concat([<DataRow key={this.state.dataBaseJSON[movieObject].id}
                                                                   item={this.state.dataBaseJSON[movieObject]} />])
        });
Ahsan Ali
  • 4,951
  • 2
  • 17
  • 27
  • Thanks for suggesting `concat` instead of `push` I've used that in a previous project, but wanted to try a new approach. Thanks for letting me know it could case issues. – Chef1075 Jan 05 '18 at 04:21