how to use props in a "depth 2" hierarchy - javascript

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

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 pass state to props in React

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.

How to display different components when clicking a button on one page?

I am trying to display different Components when I click a button with my OnClick in the Render Function of my App.js.
I would like to show a certain component when the button is clicked and it hides the other components.
this is an example of what I want to do
return (
<div className={styles.container}>
<img className={styles.image} src={image} alt="COVID-19" />
//If the Country Button which is the default is clicked show This
<ThemeProvider theme = {theme}>
<CountryPicker handleCountryChange={this.handleCountryChange} />
<CountryCards CountryData = {CountryData} CountryYesterdayData = {CountryYesterdayData}/>
</ThemeProvider>
<Chart countrydailydata ={countrydailydata} />
//If the State Button is clicked show this
<ThemeProvider theme= {theme}>
<StatePicker handleStateChange={this.handleStateChange} />
<StateCards stateData= {stateData} yesterdayStateData = {yesterdayStateData}/>
</ThemeProvider>
//If the City Button is clicked show this
<CityPicker handleCityChange={this.handleCityChange}/>
<CityCard cityData = {cityData}/>
</div>
);
import React from 'react';
import logo from './logo.svg';
import './App.css';
function Statepicker(){
return(
<div>Statepicker</div>
)
}
function Statecards(){
return(
<div>Statecards</div>
)
}
function Countrypicker(){
return(
<div>Countrypicker</div>
)
}
function Countrycards(){
return(
<div>Countrycards</div>
)
}
class ThemeProvider extends React.Component{
constructor(props){
super(props);
this.state={country:true,states:false}
}
renderCountryOrState=()=>{
if(this.state.states){
return(<React.Fragment>
<Statepicker/>
<Statecards/>
</React.Fragment>)
}
return (
<React.Fragment>
<Countrypicker/>
<Countrycards/>
</React.Fragment>
)
}
render(){
return(
<div>
<button onClick={(e)=>{this.setState({country:true,states:false})}}>Select country</button>
<button onClick={(e)=>{this.setState({country:false,states:true})}}>Select state</button>
{this.renderCountryOrState()}
</div>
)
}
}
function App() {
return (
<ThemeProvider/>
);
}
export default App;
import React from "react";
import { Button, View } from "react-native";
import styles from "./App.module.css";
import {View} from 'react-native'
import image from "./images/covid1.png";
class App extends React.Component {
constructor(props){
super(props);
state = {
CityButton: false,
StateButton: false,
CountryButton: false
};
}
render() {
{ CountryButton, StateButton, CityButton } = this.state;
return (
<view>
<div className={styles.container}>
<img className={styles.image} src={image} alt="COVID-19" />
<Button title="Country Mode" onPress={() =>
this.setState({CountryButton: true}
)} />
<Button title="State Mode" onPress={() =>
this.setState({StateButton: true}
)} />
<Button title="County Mode" onPress={() =>
this.setState({CityButton: true}
)} />
{CountryButton && <div> Hello </div>}
{StateButton && <div> Hello </div>}
{CityButton && <div> Hello </div>}
</div>
</view>
);
}
}
export default App;

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} />}
/>
)

React DnD drags whole list of cards instead of single card

I am trying to use react DnD in my react Project. In my render method I define a variable named Populate like show below, which returns a list of cards like this
render() {
var isDragging = this.props.isDragging;
var connectDragSource = this.props.connectDragSource;
var Populate = this.props.mediaFiles.map((value) => {
return(
<div>
<MuiThemeProvider>
<Card style= {{marginBottom: 2, opacity: isDragging ? 0 : 1}} id={value.id} key={value.id}
onMouseOver={this.onMouseOver}
onMouseOut={this.onMouseOut}
//onTouchTap={() => {this.handleClick(value.id)}}
zDepth={this.state.shadow}>
<CardHeader
title={value.Episode_Name}
//subtitle={value.description}
actAsExpander={false}
showExpandableButton={false}
/>
</Card>
</MuiThemeProvider>
</div>
)
});
And my return of render method looks like this
return connectDragSource (
<div>
<MuiThemeProvider>
<div className="mediaFilesComponent2">
{Populate}
</div>
</MuiThemeProvider>
</div>
)
Problem is when I try using drag, then the whole list of cards gets selected for drag. I want all the cards having individual drag functionality.
If you want each card to have drag functionality than you'll have to wrap each card in a DragSource, and not the entire list. I would split out the Card into it's own component, wrapped in a DragSource, like this:
import React, { Component, PropTypes } from 'react';
import { ItemTypes } from './Constants';
import { DragSource } from 'react-dnd';
const CardSource = {
beginDrag: function (props) {
return {};
}
};
function collect(connect, monitor) {
return {
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging()
}
}
class CardDragContainer extends React.Component {
render() {
return this.props.connectDragSource(
<div>
<Card style= {{marginBottom: 2, opacity: this.props.isDragging ? 0 : 1}} id={value.id} key={value.id}
onMouseOver={this.props.onMouseOver}
onMouseOut={this.props.onMouseOut}
zDepth={this.props.shadow}>
<CardHeader
title={props.title}
actAsExpander={false}
showExpandableButton={false}
/>
</Card>
</div>
)
}
}
export default DragSource(ItemTypes.<Your Item Type>, CardSource, collect)(CardDragContainer);
Then you would use this DragContainer in render of the higher level component like this:
render() {
var Populate = this.props.mediaFiles.map((value) => {
return(
<div>
<MuiThemeProvider>
<CardDragContainer
value={value}
onMouseOver={this.onMouseOver}
onMouseOut={this.onMouseOut}
shadow={this.state.shadow}
/>
</MuiThemeProvider>
</div>
)
});
return (
<div>
<MuiThemeProvider>
<div className="mediaFilesComponent2">
{Populate}
</div>
</MuiThemeProvider>
</div>
);
}
That should give you a list of Cards, each of which will be individually draggable.

Categories

Resources