How to pass state to props in React - javascript

I'm new to React and trying to make a project that uses a dishes.js file as a parameter in MenuComponent.js to use in a DishdetailComponent.js. However, when I compile, it gives nothing without any error or warning. MenuComponent.js is my parent and DishdetailComponent.js is my child in my code.
MenuComponent.js:
import React, { Component } from 'react';
import DishDetail from './DishdetailComponent';
import { DISHES } from './dishes';
class Menu extends Component {
constructor(props) {
super(props);
this.state = {
dishes: DISHES
};
}
render() {
return (
<DishDetail dishes = {this.state.dishes} />
)
}
}
export default Menu;
DishdetailComponent.js:
function renderDish({ dish }) {
if (dish != null) {
const dishList = dish.map((Dish) => {
return (
<div className="col-12 col-md-5 m-1">
<Card>
<CardImg width="100%" object src={Dish.image} alt={Dish.name}></CardImg>
<CardBody>
<CardTitle>{Dish.name}</CardTitle>
<CardText>{Dish.description}</CardText>
</CardBody>
</Card>
</div>
);
});
return (
{dishList}
);
} else {
return <div></div>;
}
}
function renderComments(comments) {
if (comments != null) {
const commentsList = comments.map((Comment) => {
return (
<div className="container">
<li key={Comment.id}>
<p>{Comment.Comment}</p>
<p>
-- {Comment.author},
{new Intl.DateTimeFormat("en-US", { year: "numeric", month: "short", day: "2-digit" }).format(new Date(Date.parse(Comment.id)))}
</p>
</li>
</div>
);
});
return (
<div className="col-12 col-md-5 m-1">
<h3>Comments</h3>
<ul className="list-unstyled">{commentsList}</ul>
</div>
);
} else {
return <div></div>;
}
}
const DishDetail = (props) => {
if (props.dish != null) {
return (
<div className="row">
<renderDish dish={props.dish} />
<renderComments comments={props.dish.comments} />
</div>
);
} else {
return <div></div>;
}
};
export default DishDetail;
App.js, where I call Menu:
import './App.css';
import { Navbar, NavbarBrand } from 'reactstrap';
import React, {Component} from 'react';
import Menu from './components/MenuComponent';
class App extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="App">
<Navbar dark color="primary">
<div className="container">
<NavbarBrand href="/">Ristorante Con Fusion</NavbarBrand>
</div>
</Navbar>
<Menu />
</div>
);
}
}
export default App

You have typo:
MenuComponent.js:
render() {
return (
<DishDetail dishes = {this.state.dishes} />
)
}
DishdetailComponent.js:
function renderDish({ dish }) {
Change to dishes, it should work.

Related

Adding a new DishdetailComponent to React application to displays the details of a Specific dish

I am new to React and I am facing a issue to get item details when an item is clicked in React from an another Dishdetail component. I have MenuComponent.js which is supposed to call the DishDetailComponent.js when any of the card is clicked, however I am getting no response, nothing happens on clicking.
Here is the code of Menu Component used to call the Dish detail Component:
import { div } from 'prelude-ls';
import React, { Component } from 'react';
import { Card, CardImg, CardBody, CardTitle, CardText, CardImgOverlay } from 'reactstrap';
import DishDetail from './DishdetailComponent';
//Adding a new component
class Menu extends Component {
constructor(props) {
super(props);
this.state = {
selectedDish: null,
}
}
//By this we change the state
onDishSelect(dish) {
this.setState({ selectedDish: dish });
}
render() {
const menu = this.props.dishes.map((dish) => {
return (
<div key={dish.id} className="col-12 col-md-5 m-1">
<Card onClick={() => this.onDishSelect(dish)}>
<CardImg width="100%" src={dish.image} alt={dish.name} />
<CardImgOverlay>
<CardTitle>{dish.name}</CardTitle>
</CardImgOverlay>
</Card>
</div>
);
});
return (
<div className="container">
<div className="row">
{menu}
</div>
{/* This will render the dish on which we have clicked */}
<div className="row">
<div className="col-12 ml-1">
<DishDetail />
</div>
</div>
</div>
);
}
}
export default Menu;
And Here is my DishdetailComponent :
import React, { Component } from 'react';
import { Card, CardImg, CardBody, CardTitle, CardText, CardImgOverlay } from 'reactstrap';
import { DISHES } from '../shared/dishes';
// Adding DishDetail component
class DishDetail extends Component {
constructor(props) {
super(props);
this.state = {
dishes: DISHES,
};
}
renderDish(dish) {
if (dish != null) {
return (
<div key={dish.id} className="row">
<div className="col-12 col-md-5 m-1">
<Card>
<CardImg width="100%" src={dish.image} alt={dish.name} />
<CardBody>
<CardTitle>{dish.name}</CardTitle>
<CardText>{dish.description}</CardText>
</CardBody>
</Card>
</div>
</div>
)
}
else {
return (
<div></div>
)
}
}
renderComments(comments) {
if(comments==null){
return(
<div></div>
)
}
const showComments = comments.map((cmnt) => {
return(
<li key={cmnt.id}>
<p>cmnt.comment</p>
<p>--cmnt.author,cmnt.date</p>
</li>
)
});
return(
<div className="col-12 col-md-5 m-1">
<h3>Comments</h3>
{showComments}
</div>
)
}
render() {
const dish = this.props.dish;
if(dish == null)
{
return(
<div></div>
)
}
const dishItem = this.renderDish(dish);
const dishComment = this.renderComments(dish.comments);
<div className="container">
{dishItem}
{dishComment}
</div>
}
}
export default DishDetail;
You have to bind your onDishSelect function.
See below:
import { div } from 'prelude-ls';
import React, { Component } from 'react';
import { Card, CardImg, CardBody, CardTitle, CardText, CardImgOverlay } from 'reactstrap';
import DishDetail from './DishdetailComponent';
//Adding a new component
class Menu extends Component {
constructor(props) {
super(props);
this.state = {
selectedDish: null,
}
// BINDING YOUR FUNCTION
this.onDishSelect = this.onDishSelect.bind(this);
}
//By this we change the state
onDishSelect(dish) {
this.setState({ selectedDish: dish });
}
render() {
const menu = this.props.dishes.map((dish) => {
return (
<div key={dish.id} className="col-12 col-md-5 m-1">
<Card onClick={() => this.onDishSelect(dish)}>
<CardImg width="100%" src={dish.image} alt={dish.name} />
<CardImgOverlay>
<CardTitle>{dish.name}</CardTitle>
</CardImgOverlay>
</Card>
</div>
);
});
return (
<div className="container">
<div className="row">
{menu}
</div>
{/* This will render the dish on which we have clicked */}
<div className="row">
<div className="col-12 ml-1">
<DishDetail />
</div>
</div>
</div>
);
}
}
export default Menu;
This should fix your issue.
If you dont want to bind all your declared fuctions, then use ES6 syntax.
i.e define your onDishSelect function like this:
onDishSelect = () => {
this.setState({ selectedDish: dish });
}
you haven't passed props to DishDetail component, it should be:
<DishDetail dish={this.state.selectedDish} />
and also do the implementations suggested by user: #Sankshit Pandoh
I found the following error in the render() method of DishdetailComponent - the dishDetail object was not being returned if dish != null:
render() {
const dish = this.props.dish;
if(dish == null)
{
return(
<div></div>
)
} else {
const dishItem = this.renderDish(dish);
const dishComment = this.renderComments(dish.comments);
return (
<div className="container">
{dishItem}
{dishComment}
</div>
)
}
}

how to use props in a "depth 2" hierarchy

I'm trying to use the props of the App.js file into DishDetailComponent.js but i have other component in the medium of these two, the assigment say:
In this task you will be adding a new DishdetailComponent to your React application and include the component into the menu component's view so that the details of a specific dish are displayed there:
Replace the card showing the selected dish in MenuComponent's view with the DishdetailComponent, and make sure to pass the selected dish information as props to the DishdetailComponent.
Create a new DishDetail class in a file named DishdetailComponent.js in the components folder
Export the DishDetail class from this file so that it can be imported in MenuComponent.js and used to construct the view of the selected dish.
Return a from the render() function. This should use the Bootstrap row class to position the content within the . This div will display both the details of the dish in a Card and the list of comments side-by-side for medium to extra large screens, but will stack them for xs and sm screens.
The card should be enclosed inside a appropriate Bootstrap column classes so that it occupies the entire 12 columns for the xs and sm screen sizes, and 5 columns for md screens and above. Also apply a class of m-1 to this div.
The comments should be enclosed in a to which you apply appropriate column classes so that it occupies the entire 12 columns for the xs and sm screen sizes, and 5 columns for md screens and above. Also apply a class of m-1 to this div.
If the dish is null then you should return an empty
App.js
import MenuComponent from './component/MenuComponent'
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
dishes: DISHES
}
}
render() {
return (
<div className="app">
<Navbar dark color="primary">
<div className="container">
<NavbarBrand href="/">
Ristorante Con Fusion
</NavbarBrand>
</div>
</Navbar>
<MenuComponent
dishes={this.state.dishes}
/>
</div>
);
}
}
export default App;
MenuComponent.js
import React, { Component } from 'react';
import { Card, CardImg, CardImgOverlay, CardText, CardBody, CardTitle } from 'reactstrap';
/* Components */
import DishDetail from './DishDetailComponent'
class MenuComponent extends Component {
constructor(props) {
super(props);
this.state = {
selectedDish: null
};
console.log('Menu Component constructor is invoked');
}
componentDidMount() {
console.log('Menu Component componentDidMount is invoked')
}
onDishSelect(dish) {
this.setState({ selectedDish: dish });
}
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>
)
}
}
render() {
const menu = this.props.dishes.map((dish) => { /* This is a prop that are provided from App.js
in the render() <MenuComponent dishes={}> */
return (
<div key={dish.id} className="col-12 col-md-5 m-1">
<Card onClick={() => this.onDishSelect(dish)}>
<CardImg width="100%" src={dish.image} alt={dish.name} />
<CardImgOverlay>
<CardTitle><strong>{dish.name}</strong></CardTitle>
</CardImgOverlay>
</Card>
</div>
);
});
console.log('Menu component render is invoked')
return (
<div className="container">
<div className="row">
{menu}
</div>
<div className="row col-12 col-md-5 col-lg-5 mr-1">
<div>
<Card className="">
{this.renderDish(this.state.selectedDish)}
</Card>
</div>
</div>
</div>
);
}
}
export default MenuComponent;
DishDetailComponent.js
/* Modules */
import React, { Component } from 'react';
import { Card, CardText, CardBody, CardTitle, CardImg } from 'reactstrap';
/* Components */
/* Styles */
class DishDetailComponent extends Component {
constructor(props) {
super(props)
this.state = {
}
}
onDishSelect(dish) {
this.setState({ selectedDish: dish })
}
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>
)
}
}
render() {
const comment = this.props.dishes.map((dish) => {
return (
<div key={dish.key}>
<ul className="unorder-list">
{dish.comments}
</ul>
</div>
)
})
return (
<div className="row col-12 col-lg-5 m-1">
<Card >
HEre i have to put the info the name the img and these things
</Card>
</div>
);
}
}
export default DishDetailComponent;
This is the last component im tried to do it but i dont know how to use very well props!
if you want see my git https://gitlab.com/folayao/react-coursera-felipe

How to update image src when opening modal

I have this class component and I'm getting the data from a JSON file by using GraphQL. Everything works as expected but I find hard to update the image src inside of the Modal component when it's open. Data doesn't seems to get passed to the Modal and it shows the same image for all the cards. If I try using props it returns undefined in the image.src.
Any ideas or help on how to solve this would be great!!
my code:
import React from "react"
import { StaticQuery, graphql } from 'gatsby'
import { Container, Row, Col } from 'react-grid-system'
import Modal from 'react-responsive-modal'
class ProjectsList extends React.Component {
constructor(props) {
super(props)
this.state = {
open: false,
}
}
onOpenModal = () => {
this.setState({ open: true, modalImage: this.props });
};
onCloseModal = () => {
this.setState({ open: false });
};
render() {
const projects = this.props;
const { open } = this.state;
return(
<div>
<Row>
{projects.projects.map(item =>(
<Col md={6} key={item.id}>
<div className="project-card">
<div className="project-img-wrap">
<img src={item.image.src.publicURL} alt="projects" onClick={this.onOpenModal} />
</div>
<div className="project-text-wrap">
<span className="project-title">{item.title}</span>
</div>
</div>
<Modal open={open} onClose={this.onCloseModal} center>
<img style={{maxWidth: '800px'}} src={item.image.src.publicURL} alt="projects" />
</Modal>
</Col>
))}
</Row>
</div>
);
}
}
export default props => (
<StaticQuery
query={graphql`
query {
dataJson {
projects {
id
title
image {
src {
publicURL
}
}
}
}
}
`}
render={({ dataJson }) => <ProjectsList projects={dataJson.projects} {...props} />}
/>
)
I've made little edits to your code. That should work out.
The problem is that you haven't passed modalImage from your state to src in Modal image.
import React from "react"
import { StaticQuery, graphql } from 'gatsby'
import { Container, Row, Col } from 'react-grid-system'
import Modal from 'react-responsive-modal'
class ProjectsList extends React.Component {
constructor(props) {
super(props)
this.state = {
open: false,
modalImage: ""
}
}
onOpenModal = (image) => {
this.setState({ open: true, modalImage: image });
};
onCloseModal = () => {
this.setState({ open: false });
};
render() {
const projects = this.props;
const { open, modalImage } = this.state;
return(
<div>
<Row>
{projects.projects.map(item =>(
<Col md={6} key={item.id}>
<div className="project-card">
<div className="project-img-wrap">
<img src={item.image.src.publicURL} alt="projects" onClick={() => this.onOpenModal(item.image.src.publicURL)} />
</div>
<div className="project-text-wrap">
<span className="project-title">{item.title}</span>
</div>
</div>
<Modal open={open} onClose={() => this.onCloseModal()} center>
<img style={{maxWidth: '800px'}} src={modalImage} alt="projects" />
</Modal>
</Col>
))}
</Row>
</div>
);
}
}
export default props => (
<StaticQuery
query={graphql`
query {
dataJson {
projects {
id
title
image {
src {
publicURL
}
}
}
}
}
`}
render={({ dataJson }) => <ProjectsList projects={dataJson.projects} {...props} />}
/>
)

"this.context" returns an empty object

I can't figure out why:
I have react _ react-dom 16.8x
I have a component between my Provider and my Consumer to avoid cyclic call between the two aforementioned elements.
I would my context be accessible directly in lifecycle component and seen ReactJS propose the this.context's method to do that.
Here my sandbox
Here my reactjs snippet
import React, {Component}from "react";
import "./App.css";
// first we will make a new context
const MyContext = React.createContext();
// Then create a provider Component
class MyProvider extends Component {
state = {
name: 'Wes',
age: 100,
cool: true
}
render() {
return (
<MyContext.Provider value={{
state: this.state,
growAYearOlder: () => this.setState({
age: this.state.age + 1
})
}}>
{this.props.children}
</MyContext.Provider>
)
}
}
const Family = (props) => (
<div className="family">
<Person />
</div>
)
class Person extends Component {
componentDidMount(){
console.log("context: ", this.context)
}
render() {
console.log("context: ", this.context)
return (
<div className="person">
<MyContext.Consumer>
{(context) => (
<React.Fragment>
<p>Age: {context.state.age}</p>
<p>Name: {context.state.name}</p>
<button onClick={context.growAYearOlder}>🍰🍥🎂</button>
</React.Fragment>
)}
</MyContext.Consumer>
</div>
)
}
}
class App extends Component {
render() {
return (
<MyProvider>
<div>
<p>I am the app</p>
<Family />
</div>
</MyProvider>
);
}
}
export default App;
Why this.context is empty?
Any hint would be great,
thanks
You need to set contextType to consume a context.
On the react website it specifies as much anyway.
So the only change required:
class Person extends Component {
componentDidMount(){
console.log("context: ", this.context)
}
render() {
console.log("context: ", this.context)
return (
<div className="person">
<MyContext.Consumer>
{(context) => (
<React.Fragment>
<p>Age: {context.state.age}</p>
<p>Name: {context.state.name}</p>
<button onClick={context.growAYearOlder}>🍰🍥🎂</button>
</React.Fragment>
)}
</MyContext.Consumer>
</div>
)
}
}
Person.contextType = MyContext;
Hope this helps!
If you need access to MyContext in Person, you can wrap Person in the consumer and pass in context when you render it in Family:
const Family = (props) => (
<div className="family">
<MyContext.Consumer>
{(context) => {
<Person context={context} />
}}
</MyContext.Consumer>
</div>
)
class Person extends Component {
componentDidMount(){
console.log("context: ", this.props.context)
}
render() {
const { context } = this.props;
console.log("context: ", context)
return (
<div className="person">
<React.Fragment>
<p>Age: {context.state.age}</p>
<p>Name: {context.state.name}</p>
<button onClick={context.growAYearOlder}>🍰🍥🎂</button>
</React.Fragment>
</div>
)
}
}

How to get data from react context

I have a React class called GlobalDataProvider:
import React, { Component } from 'react';
const DataContext = React.createContext();
export default DataContext;
export class DataProvider extends Component {
state = {
title: 'Some title'
}
render() {
return (
<DataContext.Provider
value={{state: this.state}}>
{this.props.children}
</DataContext.Provider>
)}
}
And I am trying to use data in another file "PageSection.js" like this:
import React from 'react';
import DataContext from '../data/GlobalDataProvider';
const PageSection= () =>{
return (
<section className="page-section">
<DataContext.Consumer>
{(context) => (
<h1>{ context.state.title }</h1>
)}
</DataContext.Consumer>
</section>
);
};
However this does not work for some reason. I get this error message:
TypeError: Cannot read property 'state' of undefined,
in PageSection.js line 11 (the line with this code: { context.state.title }).
What am I doing wrong?
Do I have to import the class DataProvider? or only the Context variable?
I suspect you need your DataContext.Consumer to be a child element of the DataContext.Provider. Something like this...
import React from 'react';
import DataContext, { DataProvider } from '../data/GlobalDataProvider';
const PageSection= () =>{
return (
<section className="page-section">
<DataProvider>
<DataContext.Consumer>
{(context) => (
<h1>{ context.state.title }</h1>
)}
</DataContext.Consumer>
</DataProvider>
</section>
);
};
try:
export class DataProvider extends Component {
state = {
title: 'Some title'
}
render() {
return (
<DataContext.Provider
value={this.state}>
{this.props.children}
</DataContext.Provider>
)}
}
const PageSection= () =>{
return (
<DataProvider>
<section className="page-section">
<DataContext.Consumer>
{(context) => (
<h1>{ context.title }</h1>
)}
</DataContext.Consumer>
</section>
</DataProvider>
);
};

Categories

Resources