Warning: Functions are not valid as a React child. or how to pass the id into element in Route tag

Viewed 41

Actually, I am trying to render the DishDetail component, there is an error like "Warning: Functions are not valid as a React child." Therefore, I cannot render the DishDetail Component under "/menu/:dishId".

Anyone can help me? I have tried googling it. The "DishDetailComponent" and "MainComponent" are below respectively. Thanks a lot!

The MainComponent:

import React, { Component, useContext} from 'react';
import Home from './HomeComponent';
import Menu from './MenuComponent';
import Contact from "./ContactComponent";
import DishDetail from './DishdetailComponent';
import Header from './HeaderComponent';
import About from './AboutComponent';
import Footer from './FooterComponent';
 import { Routes, Route, Navigate, useParams} from 'react-router-dom';
import {connect} from 'react-redux';


const mapStateToProps = (state) => {
return {
    dishes: state.dishes,
    comments: state.comments,
    leaders: state.leaders,
    promotions: state.promotions
}}




class Main extends Component{
  constructor(props) {
   super(props);}

  render() {

const HomePage =() => {
    return(
        <Home 
        dish={this.props.dishes.filter((dish) => dish.featured)[0]}
        promotion={this.props.promotions.filter((promo) => promo.featured)[0]}
        leader={this.props.leaders.filter((leader) => leader.featured)[0]}
        />
    );
}

const DishWithId = ({match}) => {
    return(
        <DishDetail dish={this.props.dishes.filter((dish) => dish.id === parseInt(match.params.dishId, 10))[0]} 
        comments={this.props.comments.filter((comment) => comment.dishId === parseInt(match.params.dishId, 10))} 
        />

    );
  };  


return (
<div>
<Header/>
<Routes>
    <Route path ="/home" element={HomePage()} />
    <Route exact path="/menu" element={<Menu dishes={this.props.dishes} />} />
    <Route path='/menu/:dishId' element={<DishWithId />} />
    <Route exact path="/contactus" element={<Contact />} />
    <Route exact path="/aboutus" element={<About leaders={this.props.leaders}/>} />
    <Route path="*" element={<Navigate replace to="/home" />} />
</Routes>
<Footer />
</div>


);}}

export default connect(mapStateToProps)(Main);

The DishDetailComponent:

import React from 'react'; 
import { Card, CardImg, CardText, CardBody, CardTitle , Breadcrumb, BreadcrumbItem} from 'reactstrap';

import { Link } from 'react-router-dom';

function RenderDish({dish}) {
    console.log('Menu Component render is invoked')
    return(
        <div className="col-12 col-md-5 m-1">
            <Card>
                <CardImg width="100%" object src={dish.image} alt={dish.name} />
                <CardBody>
                    <CardTitle>{dish.name}</CardTitle>
                    <CardText>{dish.description}</CardText>
                </CardBody>
            </Card>
        </div>
        
    );
}


function RenderComments({comments}) {
    const comment = comments.map((comment)=> {
        return (
            <div key={comment.id} className="col-12 col-md-5 m-1">
                
                <p>{comment.comment}</p>
                <p>--{comment.author} ,   {new Intl.DateTimeFormat('en-US', {year: 'numeric', month: 'long', day: '2-digit' }).format(new Date(comment.date))}</p>
            </div>
        )
    });
    return(
        <div className="col-12 col-md-5 m-1">
            <h4>Comment</h4>
            {comment}
        </div>
    );
}


const DishDetail = (props) => {
    if (props.dish != null) {
        return (
            <div className='container'>
            <div className='row'>
                <Breadcrumb>
                <BreadcrumbItem><Link to="/menu">Menu</Link></BreadcrumbItem>
                <BreadcrumbItem active>{props.dish.name}</BreadcrumbItem>
                </Breadcrumb>
                <div className='col-12'>
                    <h3>{props.dish.name}</h3>
                    <hr />
                </div>
            </div>
                <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.comments} />
                    </div>
                </div>
            </div>
        );
    }
    else {
        return (
            <div></div>
        )
    }
} export default DishDetail;

    
1 Answers

HomePage and DishWithId are function components, so this is simply undefined in both cases. Also, you should completely avoid declaring React components within other React components. They should be declared outside on their own. The filtering should occur in the parent component and the filtered result should be passed as a prop to the routed component.

Example:

import React, { Component, useContext } from 'react';
import Home from './HomeComponent';
import Menu from './MenuComponent';
import Contact from "./ContactComponent";
import DishDetail from './DishdetailComponent';
import Header from './HeaderComponent';
import About from './AboutComponent';
import Footer from './FooterComponent';
import { Routes, Route, Navigate, useParams } from 'react-router-dom';
import { connect } from 'react-redux';

const DishWithId = ({ dishes, comments }) => {
  const { dishId } = useParams();

  return (
    <DishDetail
      dish={dishes.find((dish) => String(dish.id) === dishId)} 
      comments={comments.find((comment) => String(comment.dishId) === dishId)} 
    />
  );
}; 

class Main extends Component{
  constructor(props) {
   super(props);
  }

  render() {
    return (
      <div>
        <Header/>
        <Routes>
          <Route
            path="/"
            element={(
              <Home 
                dish={this.props.dishes.find((dish) => dish.featured)}
                promotion={this.props.promotions.find((promo) => promo.featured)}
                leader={this.props.leaders.find((leader) => leader.featured)}
              />
            )}
          />
          <Route
            path="/menu"
            element={<Menu dishes={this.props.dishes} />}
          />
          <Route
            path='/menu/:dishId'
            element={(
              <DishWithId
                dishes={this.props.dishes}
                comments={this.props.comments}
              />
            )}
          />
          <Route path="/contactus" element={<Contact />} />
          <Route path="/aboutus" element={<About leaders={this.props.leaders}/>} />
          <Route path="*" element={<Navigate replace to="/home" />} />
        </Routes>
        <Footer />
      </div>
    );
  }
}

const mapStateToProps = (state) => ({
  dishes: state.dishes,
  comments: state.comments,
  leaders: state.leaders,
  promotions: state.promotions
});

export default connect(mapStateToProps)(Main);

It would be better to ditch the connect HOC and use the useSelector hook directly in the components that need to access the state. This avoid the odd props drilling issue.

Example:

Home

import React from 'react';
import { useSelector } from 'react-redux';

const Home = () => {
  const dishes = useSelector(state => state.dishes);
  const leaders = useSelector(state => state.leaders);
  const promotions = useSelector(state => state.promotions);

  const dish = dishes.find((dish) => dish.featured);
  const promotion = promotions.find((promo) => promo.featured);
  const leader = leaders.find((leader) => leader.featured);

  return .....;
};

DishDetail

import React from 'react';
import { useParams } from 'react-router-dom';
import { useSelector } from 'react-redux';

const DishDetail = () => {
  const { dishId } = useParams();
  const dishes = useSelector(state => state.dishes);
  const comments = useSelector(state => state.comments);

  const dish = dishes.find((dish) => String(dish.id) === dishId);
  const comments = comments.find((comment) => String(comment.dishId) === dishId);

  return <DishDetail {...{ comments, dish }} />;
};

... etc for other routed components ...

App

import React, { Component, useContext } from 'react';
import Home from './HomeComponent';
import Menu from './MenuComponent';
import Contact from "./ContactComponent";
import DishDetail from './DishdetailComponent';
import Header from './HeaderComponent';
import About from './AboutComponent';
import Footer from './FooterComponent';
import { Routes, Route, Navigate } from 'react-router-dom';

class Main extends Component{
  constructor(props) {
   super(props);
  }

  render() {
    return (
      <div>
        <Header/>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/menu" element={<Menu />}/>
          <Route path='/menu/:dishId' element={<DishWithId />}/>
          <Route path="/contactus" element={<Contact />} />
          <Route path="/aboutus" element={<About />} />
          <Route path="*" element={<Navigate replace to="/home" />} />
        </Routes>
        <Footer />
      </div>
    );
  }
}

export default Main;
Related