Search code examples
reactjsjsxreactstrap

How Do I Fix This TypeError In React?


I am struggling and in need of help! I'm extremely new to React - and I'm busy doing this practice assignment for a course that I'm taking to learn it, but I am running into the same sort of problem over and over again and I cannot seem to get it right.

I do not need you to complete my assignment, but any hint or directions/feedback on my code on where I am going wrong and what I am doing wrong would be highly appreciated. Oof, I'm just so tired of not finding the solution on Google/forums, and my React skills are fairly novice so I can't really identify the problem without the help of some super cool React experts out there! Thank you very much in advance. :)

I keep on getting the error: TypeError: Cannot read property 'map' of undefined

Full Source Code Here: https://github.com/christinec-dev/react_new

The overall end goal of my application is as follows (added here for perspective reasons):

Task 1:

  • A new DishdetailComponent has been added to your React application.
  • Included the DishDetail into your MenuComponent's view to show the selected dish.
  • Passing the selected dish as props to the DishDetail Component.
  • Used the appropriate Bootstrap classes to the card so that it occupies the entire row for xs and sm screen sizes, and 5 columns for md screens and above.
  • Used the appropriate Bootstrap classes to the div containing the list of comments so that it occupies the entire row for xs and sm screen sizes, and 5 columns for md screens and above.

Task 2:

  • Used the Card component to display the details of the dish.

Task 3:

  • Included a list of comments about the dish into the dishdetail view.

----------------------------------------- menuComponent.js

//package imports
import React, { Component } from 'react';
//import bootrap components from reactstrap library
//NOTE: The media query returns a CSS style 
import { Card, CardImgOverlay, CardImg, CardBody, CardText, CardTitle  } from 'reactstrap';
import DishDetail from './DishdetailComponent';


//creates Menu component
class Menu extends Component {
    
    //define a constructor for it
    constructor(props) {

        //Props is read-only and are used to pass data, whereas state is for managing data and can be modified by its own component

        //required when you create a component in react
        super(props);
        
        //when document is loaded no card has been selected by default
        this.state = {
            selectedDish: null
        };
    }

    //when dishg is clicked, it will render details of dish
    onDishSelect(dish) {
        this.setState({selectedDish: dish});
    }

   //if dish is clicked it will show card details, else nothing
   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>
            )
        }
    }
    
    //return a value or function that will be called
    render() {

        //will iterate (map) over the dishes list and return each key (item) uniquely
        const menu = this.props.dishes.map((dish) => {
            // This will create the layout of the menu items by displaying the image, name and -description- of each menu item
            return (
                <div key={dish.id} className="col-12 col-md-5 m-1">
                      {/* When clicked on, it will run event function*/} 
                      <Card onClick={() => this.onDishSelect(dish.id, dish.comments)}>
                            <CardImg width="100%" src={dish.image} alt={dish.name} />

                            <CardImgOverlay body className="ml-5">
                                <CardTitle>{dish.name}</CardTitle>
                            </CardImgOverlay>
                        </Card>
                </div>
            );
        });

        return (
        //This will return a menu list that will be defined
        <div className="container">
            <div className="row">
                 {/* This will return the menu items */}             
                    {menu}
            </div>
            <div className="row">
                 {/* This will return the clicked card dish items when clicked */} 
                 <div className="col-12 col-md-5 m-1">          
                    {this.renderDish(this.state.selectedDish)},
                </div> 
                <div className="col-12 col-md-5 m-1">          
                    <DishDetail dishes={this.state.dishes} />
                </div>  
            </div>
        </div>
        );
    }
}

//exports it for use in other files
export default Menu;
-------------------DishdetailComponent.js

//package imports
import React, { Component } from 'react';
//import bootrap components from reactstrap library
//NOTE: The media query returns a CSS style 


//creates Dishdetail component
class DishDetail extends Component {
    
    //define a constructor for it
    constructor(props) {
        //Props is read-only and are used to pass data, whereas state is for managing data and can be modified by its own component

        //required when you create a component in react
        super(props);
        
        //when document is loaded no card has been selected by default
        this.state = {
            selectedDish: null,
            comments: null
        };
    }

    onDishSelect(comments) {
        this.setState({comments: comments});
    }

   //if dish is clicked it will show card comments, else nothing
   
    renderComments(comments) {
        if (comments != null){
                return (
                        <ul key={comments.id} className="list-unstyled">
                            <li className="comment">{comments.comment}</li>
                            <li className="author"> {comments.author}</li>
                            <li className="date"> {comments.date}</li>
                        </ul>   
                )       
        }
        else {
            return(
                <div></div>
            )
        }
    }

    //return a value or function that will be called
    render() {

    //will iterate (map) over the dishes list and return each key (item) uniquely
    const details = this.props.comments.map((comments) => {
            return (         
                <div className="container">
                    <div className="row">  
                    <div className="col-12 col-md-5 m-1">
                        <h4>Comments</h4>
                        <div>{comments}</div>  
                    </div>
                    </div>
                </div>
        );
    });

    return (
        //This will return a menu list that will be defined
        <div className="container">
            <div className="row">
                 {/* This will return the menu items */}             
                    {details}
            </div>
            <div className="row">
                 {/* This will return the clicked card dish items when clicked */}             
                 {this.renderComments(this.state.selectedDish)},
            </div>
        </div>
        );
    }
}
  

      
//exports it for use in other files
export default DishDetail;
----------------------------------------- App.js

//package and component imports
import logo from './logo.svg';
import React, {Component} from 'react';
import { NavbarBrand, Navbar } from 'reactstrap';
import Menu from './components/menuComponent';
import DishDetail from './components/DishdetailComponent';

import './App.css';
import {DISHES} from './shared/dishes';

//creates Menu component
class App extends Component {
    
  //define a constructor for it
  constructor (props) {

    //Props is read-only and are used to pass data, whereas state is for managing data and can be modified by its own component

    //required when you create a component in react
    super(props);

   
    //when document is loaded no card has been selected by default
        this.state = {
          dishes: DISHES,
          selectedDish: null,
          comments: null
      };
  }

  //when dishg is clicked, it will render details of dish
  onDishSelect(dish) {
      this.setState({ selectedDish: dish});
      
  }


  render () {
    return (    
    //To create html structures in React we always define it via the className strucutre
    <div>
      
      {/* This will create a layour based on our individual component files. For example, if we have a navbarComponent file, then we just import it from there and insert it here, without having to code the whole thing. */}
      <Navbar color="primary" dark expand="md">
        <div className="container">
        <NavbarBrand href="/"> Ristorante Con Fusion</NavbarBrand>
        </div>
      </Navbar>
  
     {/* The Menu component from the menuComponent.js file is rendered here and displayed when the index.js is loaded */}
     <Menu dishes={this.state.dishes} />
    </div>
  );
  }
}

//exports it for use in other files
export default App;
enter image description here


Solution

  • I was able to solve it by myself after reading some documentation. It turns out I had to remove the renderDish(dish) function from the menuComponent.js file and place it in the DishdetailComponent.js file.

    To solve the

    TypeError: Cannot read property 'map' of undefined problem

    I did the following:

     //if dish is clicked it will show dish comments, else nothing
    renderComments(array) {
        //if dish array is not equal to null, display comments in half grid
        if (array.length != 0) {
            return (
                //col-12 for sm and xs, and col-5 for md and lg screens with margin @ 1
                <div className="col-12 col-md-5 m-1">
                    {/* Displays the comment title, and details of author, date and comment */}
                    <h4>Comments</h4>                  
                    {/* //will iterate (map) over the comments list and return each key (item) uniquely */}
                    { array.map (comment => (
                        <ul className="list-unstyled">
                            <li>
                                <p>{comment.comment}</p>
                                <p><i> - {comment.author} </i>, {comment.date}</p>
                            </li>
                        </ul>
                    ))
                    }
                </div>
            );
        }
        //if the dish contains nothing, show nothing
        else {
            return (
                <div></div>
            );
    
        }
    }