Reputation: 229
I had already found a solution by luck so I don't quite understand how it worked even after trying to read stuff online.
I am simply trying to get the array of comments inside the selectedDish prop. The selectedDish's state is populated inside the Main Component's function, and I am trying to access selectedDish's array of comments inside the DishDetail component.
I was able to get the comments by:
{ this.props.selectedDish && this.renderComments(this.props.selectedDish.comments)}
But I am unable to when I just do the following and receive this error "Cannot read property 'comments' of undefined":
{ this.renderComments(this.props.selectedDish.comments)}
Why does this work? Shouldn't this.props.selectedDish.comments
be enough as I am able to successfully render the "renderDish(dish)" function?
Main Component
import React, { Component } from 'react';
import { Navbar, NavbarBrand } from 'reactstrap';
import Menu from './MenuComponent';
import Dishdetail from './DishdetailComponent';
import { DISHES } from '../shared/dishes';
class Main extends React.Component {
constructor() {
super();
this.state = {
dishes: DISHES,
selectedDish: null
};
// this.onDishSelect = this.onDishSelect.bind(this);
}
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 selectedDish={this.state.dishes.filter((dish) => dish.id === this.state.selectedDish)[0]} />
</div>
);
}
}
export default Main;
DishDetail Component
import React, { Component } from 'react';
import { Card, CardImg, CardText, CardBody, CardTitle } from 'reactstrap';
export default class Dishdetail extends React.Component {
constructor(props) {
super(props);
}
renderDish(dish) {
if (dish != null)
return (
<Card >
<CardImg width="100%" src={dish.image} alt={dish.name} />
<CardBody>
<CardTitle>{dish.name}</CardTitle>
<CardText>{dish.description}</CardText>
</CardBody>
</Card>
);
else
return (
<div></div>
);
}
renderComments(comments) {
let list = (<div></div>);
if (comments != null) {
list = (
<ul className="list-unstyled">
{comments.map(c => {
return (
<li key={c.id}>
<p>{c.comment}</p>
<p>-- {c.author}, {new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'short', day: '2-digit' }).format(new Date(Date.parse(c.date)))}</p>
</li>
);
})}
</ul>
);
}
return (
<div>
<h4>Comments</h4>
{list}
</div>
);
}
render() {
return (
<div className="row">
<div className="col-12 col-md-5 m-1">
{this.renderDish(this.props.selectedDish)}
</div>
<div className="col-12 col-md-5 m-1">
{/* This works: */}
{ this.props.selectedDish && this.renderComments(this.props.selectedDish.comments)}
{/* This doesn't: */}
{/* { this.renderComments(this.props.selectedDish.comments)} */}
</div>
</div>
);
}
}
Upvotes: 1
Views: 289
Reputation: 2679
Just a few pointers , you don't need to have a class based component since you aren't using lifecycle methods etc.
you can use destructuring to reduce typing
let {selectedDish:{comments}} = this.props .
You could solve your problem with a default value for comments ,
`const Dishdetail = ({selectedDishes:{comments}}=[]}) => {}
and conditional rendering in render
<div>
{ comments.length > 0 &&
//map you comments here if you have some
}
</div>`
Upvotes: 1
Reputation: 3001
When your component is loaded the first time the selectedDish
state value is null. It only gets set later, hence the error.
Changing the below code
constructor() {
super();
this.state = {
dishes: DISHES,
selectedDish: null
};
// this.onDishSelect = this.onDishSelect.bind(this);
}
To
constructor() {
super();
this.state = {
dishes: DISHES,
selectedDish: {}
};
// this.onDishSelect = this.onDishSelect.bind(this);
}
will make it work. However I feel that what you are doing now i.e. null check and then render is a more robust way of doing it.
Upvotes: 1
Reputation: 4869
When your main component renders for the first time the props
are still undefined
as DISHES haven't been loaded, reasons for that might be the DISHES are initiated with some external resource.
So when this.props.selectedDish
is undefined
you can't access it's key because they don't exist. So react throws this error.
{ this.props.selectedDish && this.renderComments(this.props.selectedDish.comments)}
So this ensures this.props.selectedDish
exists and then you are accessing it's comments
key.
Upvotes: 2