chsm19
chsm19

Reputation: 25

Why am i getting this error in my React application?

I am getting this error when I compile my react application: TypeError: Cannot read properties of undefined (reading 'comments').

The part of the code that's causing this is the line 85 of my Dishdetailcomponent.js

Dishdetailcomponent.js

import { Card, CardImg, CardImgOverlay, CardText, CardBody,
    CardTitle } from 'reactstrap';

class DishDetail extends Component {

    constructor(props) {
        super(props);

    }

    componentDidMount(){
        console.log('Dishdetail componentDidMount is invoked');
    }

    componentDidUpdate(){
        console.log('Dishdetail componentDidMount is invoked');
    }

    componentWillReceiveProps(props) {
        this.setState({ props: props })
      }

    renderDish(dish) {
        if (dish != null)
            return(
                <Card>
                        <CardImg top src={dish.image} alt={dish.name} />
                            <CardBody>
                                <CardTitle>{dish.name}</CardTitle>
                                <CardText>{dish.description}</CardText>
                            </CardBody>
                    </Card>
            );
        else
            return(
                <div></div>
            );
    }

    renderComments(comments){
        console.log(comments);
        var commentsL = {}
        if (comments != null){
            commentsL = comments.map((comments) => {
            return (
            <ul class="list">
            <uli class="list-item" id = {comments.id}>
                    <div className="row">
                        {comments.comment}
                    </div>
                    <div className="row">
                        <p>--</p>{comments.author}&nbsp;{new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'short', day: '2-digit'}).format(new Date(Date.parse(comments.date)))}
                    </div>
            </uli>
            </ul>

        );
        }); }

        if (comments != null)
            return(
              <div>
                  <h4>Comments</h4>
                  {commentsL}
              </div>  
            );
        else
            return(
                <div></div>
            );
    }

    render() {
        console.log(this.props);
        console.log('Dishdetail component render is invoked');
        if (this.props != 'undefined')
            return (
            <div className="container">
                <div className="row">
                  <div  className="col-12 col-md-5 m-1">
                    {this.renderDish(this.props.dish)}
                  </div>
                  <div  className="col-12 col-md-5 m-1">
                    {this.renderComments(this.props.dish.comments)}
                  </div>
                </div>
            </div>
            );
    }
}

export default DishDetail; 

The component above is the presentation component that should show the selected dish at the end of the page.

Maincomponent.js

import { Navbar, NavbarBrand } from 'reactstrap';
import Menu from './MenuComponent';
import DishDetail from './DishdetailComponent';
import { DISHES } from '../shared/dishes';

class Main extends Component {

  constructor(props) {
    super(props);
    this.state = {
        dishes: DISHES,
        selectedDish: null
    };
  }

  onDishSelect(dishId) {
    this.setState({ selectedDish: dishId});
  }

  render() {
    return (
      <div>
        <Navbar dark color="primary">
          <div className="container">
            <NavbarBrand href="/">Ristorante Con Fusion</NavbarBrand>
          </div>
        </Navbar>
        <Menu dishes={this.state.dishes} onClick={(dishId) => this.onDishSelect(dishId)} />
        <DishDetail dish={this.state.dishes.filter((dish) => dish.id === this.state.selectedDish)[0]} />
      </div>
    );
  }
}

export default Main;

The component above is the container component that passes the props to the Dishdetailcomponent

dishes.js

    [
        {
        id: 0,
        name:'Uthappizza',
        image: 'assets/images/uthappizza.png',
        category: 'mains',
        label:'Hot',
        price:'4.99',
        description:'A unique combination of Indian Uthappam (pancake) and Italian pizza, topped with Cerignola olives, ripe vine cherry tomatoes, Vidalia onion, Guntur chillies and Buffalo Paneer.',
        comments: [
            {
            id: 0,
            rating: 5,
            comment: "Imagine all the eatables, living in conFusion!",
            author: "John Lemon",
            date: "2012-10-16T17:57:28.556094Z"
            },
            {
            id: 1,
            rating: 4,
            comment: "Sends anyone to heaven, I wish I could get my mother-in-law to eat it!",
            author: "Paul McVites",
            date: "2014-09-05T17:57:28.556094Z"
            },
            {
            id: 2,
            rating: 3,
            comment: "Eat it, just eat it!",
            author: "Michael Jaikishan",
            date: "2015-02-13T17:57:28.556094Z"
            },
            {
            id: 3,
            rating: 4,
            comment: "Ultimate, Reaching for the stars!",
            author: "Ringo Starry",
            date: "2013-12-02T17:57:28.556094Z"
            },
            {
            id: 4,
            rating: 2,
            comment: "It's your birthday, we're gonna party!",
            author: "25 Cent",
            date: "2011-12-02T17:57:28.556094Z"
            }
        ]                        },
        {
        id: 1,
        name:'Zucchipakoda',
        image: 'assets/images/zucchipakoda.png',
        category: 'appetizer',
        label:'',
        price:'1.99',
        description:'Deep fried Zucchini coated with mildly spiced Chickpea flour batter accompanied with a sweet-tangy tamarind sauce',
        comments: [
            {
            id: 0,
            rating: 5,
            comment: "Imagine all the eatables, living in conFusion!",
            author: "John Lemon",
            date: "2012-10-16T17:57:28.556094Z"
            },
            {
            id: 1,
            rating: 4,
            comment: "Sends anyone to heaven, I wish I could get my mother-in-law to eat it!",
            author: "Paul McVites",
            date: "2014-09-05T17:57:28.556094Z"
            },
            {
            id: 2,
            rating: 3,
            comment: "Eat it, just eat it!",
            author: "Michael Jaikishan",
            date: "2015-02-13T17:57:28.556094Z"
            },
            {
            id: 3,
            rating: 4,
            comment: "Ultimate, Reaching for the stars!",
            author: "Ringo Starry",
            date: "2013-12-02T17:57:28.556094Z"
            },
            {
            id: 4,
            rating: 2,
            comment: "It's your birthday, we're gonna party!",
            author: "25 Cent",
            date: "2011-12-02T17:57:28.556094Z"
            }
        ]
        },
        {
        id: 2,
        name:'Vadonut',
        image: 'assets/images/vadonut.png',
        category: 'appetizer',
        label:'New',
        price:'1.99',
        description:'A quintessential ConFusion experience, is it a vada or is it a donut?',
        comments: [
            {
            id: 0,
            rating: 5,
            comment: "Imagine all the eatables, living in conFusion!",
            author: "John Lemon",
            date: "2012-10-16T17:57:28.556094Z"
            },
            {
            id: 1,
            rating: 4,
            comment: "Sends anyone to heaven, I wish I could get my mother-in-law to eat it!",
            author: "Paul McVites",
            date: "2014-09-05T17:57:28.556094Z"
            },
            {
            id: 2,
            rating: 3,
            comment: "Eat it, just eat it!",
            author: "Michael Jaikishan",
            date: "2015-02-13T17:57:28.556094Z"
            },
            {
            id: 3,
            rating: 4,
            comment: "Ultimate, Reaching for the stars!",
            author: "Ringo Starry",
            date: "2013-12-02T17:57:28.556094Z"
            },
            {
            id: 4,
            rating: 2,
            comment: "It's your birthday, we're gonna party!",
            author: "25 Cent",
            date: "2011-12-02T17:57:28.556094Z"
            }
        ]
        },
        {
        id: 3,
        name:'ElaiCheese Cake',
        image: 'assets/images/elaicheesecake.png',
        category: 'dessert',
        label:'',
        price:'2.99',
        description:'A delectable, semi-sweet New York Style Cheese Cake, with Graham cracker crust and spiced with Indian cardamoms',
        comments: [
            {
            id: 0,
            rating: 5,
            comment: "Imagine all the eatables, living in conFusion!",
            author: "John Lemon",
            date: "2012-10-16T17:57:28.556094Z"
            },
            {
            id: 1,
            rating: 4,
            comment: "Sends anyone to heaven, I wish I could get my mother-in-law to eat it!",
            author: "Paul McVites",
            date: "2014-09-05T17:57:28.556094Z"
            },
            {
            id: 2,
            rating: 3,
            comment: "Eat it, just eat it!",
            author: "Michael Jaikishan",
            date: "2015-02-13T17:57:28.556094Z"
            },
            {
            id: 3,
            rating: 4,
            comment: "Ultimate, Reaching for the stars!",
            author: "Ringo Starry",
            date: "2013-12-02T17:57:28.556094Z"
            },
            {
            id: 4,
            rating: 2,
            comment: "It's your birthday, we're gonna party!",
            author: "25 Cent",
            date: "2011-12-02T17:57:28.556094Z"
            }
        ]
        }
    ];```

The file above contains the data of the dishes.

Upvotes: 0

Views: 173

Answers (2)

Drew Reese
Drew Reese

Reputation: 203408

Your initial state in Main is:

this.state = {
  dishes: DISHES,
  selectedDish: null
};

And you filter the dishes state into an array and pass the 0th element on to DishDetail on the dish prop:

<DishDetail
  dish={this.state.dishes.filter((dish) => 
    dish.id === this.state.selectedDish)[0]
  }
/>

During the initial render cycle the filtering returns an empty array since no dish item has a null id property, so the dish prop is undefined.

Later in the renderComments of DishDetail you attempt to pass this.props.dish.comments:

<div  className="col-12 col-md-5 m-1">
  {this.renderComments(this.props.dish.comments)}
</div>

Since this.props.dish is undefined you can't access further into the object.

Solution

However you pass a dish prop you should first check that this.props.dish and this.props.dish.comments exist before calling renderComments.

<div  className="col-12 col-md-5 m-1">
  {this.props.dish?.comments && this.renderComments(this.props.dish.comments)}
</div>

or

<div  className="col-12 col-md-5 m-1">
  {this.props.dish 
    && this.props.dish.comments
    && this.renderComments(this.props.dish.comments)}
</div>

Upvotes: 1

Viet
Viet

Reputation: 12807

Because initial value of selectedDish is null. So filter will return an empty array. And dish will be undefined.

You should check value of this.props.dish before return instead this.props

if (this.props.dish != 'undefined')
    return (

render() {
    console.log(this.props);
    console.log('Dishdetail component render is invoked');
    if (this.props.dish != 'undefined')
        return (
        <div className="container">
...

And you should use find instead of filter to find an element in the array.

<DishDetail dish={this.state.dishes.find((dish) => dish.id === this.state.selectedDish)} />

Upvotes: 0

Related Questions