'React cannot set properties of undefined in a functionnal component
Im learning react.js and its functionnal component and I have this issue with a clickable item that triggers a functionnal component : before any click, when the page is loading, i have an error Uncaught TypeError: Cannot read properties of undefined (reading 'comments'). The error is when i call {props.dish.comments} in <RenderComments...
My functional Dishdetail component code is (i removed RenderDish which is not causing an error) :
function RenderComments({ comments }) {
if (comments != null) {
const rendercomments = comments.map((comment) => {
return (
<p key={comment.id} className="text-start">
{comment.comment}
<br />
<br />
-- {comment.author},{" "}
{new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "short",
day: "2-digit",
}).format(new Date(Date.parse(comment.date)))}
<br />
<br />
</p>
);
});
return (
<div className="container">
<h4 className="text-start">Comments</h4>
{rendercomments}
</div>
);
} else {
console.log("renderComments comments is null");
return <div></div>;
}
}
const Dishdetail = (props) => {
return (
<div className="row">
<div className="col-12 col-md-5 m-1">
<RenderDish dish={props.dish} />
</div>
<div className="col-12 col-md-5 m-1">
<RenderComments comments={props.dish.comments} />
</div>
</div>
);
};
export default Dishdetail;
And my caller component is like this :
import React, { Component } from 'react';
import Menu from './MenuComponent';
import {DISHES} from '../shared/dishes'
import Dishdetail from './DishdetailComponent';
import Header from './HeaderComponent'
class Main extends Component {
constructor(props){
super(props);
this.state={dishes:DISHES,
selectedDish:null,
};
}
onDishSelect(dishId){
this.setState({selectedDish:dishId});
console.log(this.state.selectedDish)
}
render(){
return (
<div className="App">
<Header/>
<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 dishes.js is like this :
export const DISHES =
[
{
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"
},
EDIT : my Menu code is :
import React from 'react';
import { Card, CardImg,CardImgOverlay,CardText,CardBody,CardTitle } from 'reactstrap';
function RenderMenuItem({dish,onClick}){
return(
<Card onClick={()=>onClick(dish.id)}>
<CardImg width='100' src={dish.image} alt={dish.name} />
<CardImgOverlay>
<CardTitle>{dish.name}</CardTitle>
</CardImgOverlay>
</Card>
);
}
const Menu = (props) => {
const menu = props.dishes.map((dish) => {
return (
<div key={dish.id} className="col-12 col-md-5 m-1">
<RenderMenuItem dish={dish} onClick={props.onClick}/>
</div>
);
});
return (
<div className="container">
<div className="row">
{menu}
</div>
</div>
);
}
export default Menu;
I tried to do a try typeof comments undefined/catch to manually set {props.dish.comments} to null but i get an error that i cant set an only readable property.
I dont know where to put a if/else on the dish or comments to adapt the code and what is the variable status of dish and comments when i dont click...apparently 'undefined'?
If you could help me to solve this without using a class component that would be great. (Im also a beginner in javascript)
Solution 1:[1]
You can rewrite Main as a functional component using useState.
?? Do not copy dishes to local state ??
import { useState } from "react"
function Main({ dishes = [], initSelection = null }) {
const [selection, setSelection] = useState(initSelection)
const selectDish = dishId => event => { setSelection(dishId) }
return <div className="App">
<Header />
<Menu dishes={dishes} onClick={selectDish} />
{ selection == null
? null
: <Dishdetail dish={dishes.find(d => d.id == selection)} />
}
</div>
}
Note how Menu uses onClick -
function Menu({ dishes = [], onClick }) {
return <div className="Menu">
{dishes.map((dish, key) =>
<a key={key} onClick={onClick(dish.id)}>{dish.name}</a>
)}
</div>
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
