I am working on the React Recipe Box project from Free Code Camp. I have a parent component that displays recipe names, which it receives as props. The recipe name can be clicked to display a child component that then has the same props passed down to the child component to display information about the ingredients. My problem is, when I click the recipe name, the props in the parent component become undefined. Ive googled a bunch, but I cant figure out why this is happening. Has anyone run into this before?
import React, { Component } from 'react';
import ShowRecipe from './recipeDetail';
class RecipeDetail extends Component {
constructor(props){
super(props)
this.state = {
isHidden:true
}
}
render() {
return (
<div className="card">
<div className="card-header">
<h5>
<button
className="btn btn-link"
onClick={() => {
this.setState({isHidden: !this.state.isHidden})
}}
>
{this.props.recipe.recipeName}
</button>
</h5>
</div>
{ !this.state.isHidden &&
<ShowRecipe
ingredients={this.props.recipe.ingredientsList}
/>
}
</div>
);
}
}
export default RecipeDetail;
this is where the props for RecipeDetail are coming from:
import React from 'react';
import RecipeDetail from './recipeDetail';
import { Jumbotron, ListGroup } from 'react-bootstrap';
const RecipeBoxHolder = ({recipes}) =>{
const recipesItems = recipes.map((recipe) => {
console.log(recipes);
return(
<RecipeDetail
key={recipe.recipeName}
recipe={recipe}
/>
)
})
return(
<div>
<Jumbotron className="jtron">
<h5>Recipes</h5>
<ListGroup>
{recipesItems}
</ListGroup>
</Jumbotron>
</div>
)
}
export default RecipeBoxHolder;
TypeError: Cannot read property 'recipeName' of undefined
RecipeDetail.render
src/components/recipeDetail.js:22
19 | <button
20 | className="btn btn-link"
21 | onClick={() => {this.setState({isHidden:
!this.state.isHidden})}} >
> 22 | {this.props.recipe.recipeName}
23 | </button>
24 |
25 | </h5>
this comes from a different component where the user enters inputs and its passed to the main parent component for the app
let recipeObject={
recipeName: this.state.recipe,
ingredientsList: this.state.ingredients
};
component where user inputs data, takes data and passes it back up to main parent component
import React, {Component} from 'react';
import {ListGroupItem} from 'react-bootstrap';
class AddModal extends Component{
constructor(props){
super(props)
this.state={
recipe: '',
ingredients: ''
}
}
render(){
let recipeObject={
recipeName: this.state.recipe,
ingredientsList: this.state.ingredients
};
return(
<ListGroupItem>
<div className="card">
<div className="card-header">
<span>Add Recipe</span> <i className="fas fa-utensils"></i>
</div>
<div className="card-body">
<label>Recipe</label>
<input value={this.state.recipe} onChange={event =>
this.setState({recipe: event.target.value})} type="text"
className="form-control"/>
<label className="add-ingredients-label">Add Ingredients</label>
<textarea value={this.state.ingredients} onChange={event =>
this.setState({ingredients: event.target.value})}
className="form-control"></textarea>
</div>
<div className="card-footer">
<button className="close-button btn btn-outline-danger btn-
sm">
Close
<i className="fas fa-times"></i>
</button>
<button onClick={e =>{this.props.addRecipe(recipeObject)}}
className="btn btn-outline-success btn-sm">
Add Recipe
<i className="fas fa-plus"></i>
</button>
</div>
</div>
</ListGroupItem>
);
}
}
export default AddModal;
this is the main parent component:
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import RecipeBoxHolder from './components/recipeContainer';
import AddModal from './components/addRecipeModal';
import './style/index.css';
import {Button, Modal} from 'react-bootstrap';
class App extends Component{
constructor(props){
super(props);
this.state={
recipes:[],
showAddModal: false
}
}
render(){
const addRecipe = (recipeObject) =>{
this.setState(prevState => ({
recipes:[...prevState.recipes, recipeObject]}
))
}
return(
<div className="container">
<RecipeBoxHolder recipes={this.state.recipes} />
<Button
bsStyle="primary"
bsSize="large">
</Button>
<AddModal addRecipe={addRecipe} />
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));
#Esteban Trevino I put my findings into an answer because the comments become too long, however I do not have a solution as I could not reproduce your issue.
I copied your code into a create-react-app generated react scaffold, and it runs fine. In this gist you can see the file I made:
https://gist.github.com/femans/22324382a8e04390f6a0ece49b867708
I do want to point out that you should not use the recipeName as a key, because they are not unique by design. Better use the following construction:
const recipesItems = recipes.map((recipe, key) => {
return(
<RecipeDetail
key={key}
recipe={recipe}
...
Related
I'm with trouble using props on React.
See my code:
This is my App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import { Navbar, NavbarBrand } from 'reactstrap';
import Menu from './components/MenuComponent';
import './App.css';
import { DISHES } from './shared/dishes';
class App extends Component {
constructor(props) {
super(props);
this.state = {
dishes: DISHES
};
}
render() {
return (
<div>
<Navbar dark color="primary">
<div className="container">
<NavbarBrand href="/">Ristorante Con Fusion</NavbarBrand>
</div>
</Navbar>
<Menu disches={this.state.disches}/>
</div>
);
}
}
export default App;
This is my MenuContent code:
import React, { Component } from 'react';
import { Media } from 'reactstrap';
class Menu extends Component{
constructor(props){
super(props);
this.state = {
}
}
render(){
const menu = this.props.dishes.map((dish) =>{
return (
<div key={dish.id} className="col-12 mt-5">
<Media tag="li">
<Media left middle>
<Media object src={dish.image} alt={dish.name}/>
</Media>
<Media body className="ml-5">
<Media heading>{dish.name}</Media>
<p>{dish.description}</p>
</Media>
</Media>
</div>
);
});
return(
<div className="container">
<div className="row">
<Media list>
{menu}
</Media>
</div>
</div>
);
}
}
export default Menu;
And finally here the error:
TypeError: this.props.dishes is undefined
11 |
12 | render(){
13 |
> 14 | const menu = this.props.dishes.map((dish) =>{
15 | return (
16 | <div key={dish.id} className="col-12 mt-5">
<Media tag="li">
Before this I have used a local data and used this.state.dish and it works fine, but now I'm trying to get the data from another file and when I change the state for props, this error stuck me
Can somebody help please?
enter image description herei develop what's up web by using react. my scenario is when ever i execute in desktop it will be displayed in a desktop responsive and whenever i displayed in a mobile it will be displayed in a mobile responsive how to do that in react.
Below i can add each and every page i develop for displaying what's up web.i want to display in mobile like what's up app so how to develop.click the enter description here it will show the image i developed.but i need only one app to have both responsivness & web.that means one url i want whats web & mobile whats app.
ChatListHeader.js
import React, { Component } from 'react';
class ChatListHeader extends Component {
render(){
return(
<div className="chatlist--header">
<div className="chatlist--header__image">
<img src={require('/home/srinath/Desktop/important/whats up web ui complete/whats up web
ui/src/images/Srinath.png')} alt={""}/>
</div>
<div className="chatlist--header__icons">
<i className="material-icons"></i>
<i className="material-icons"></i>
</div>
</div>
)
}
}
export default ChatListHeader;
import React, { Component } from 'react';
import ChatListHeader from '../presentational/ChatListHeader';
import ChatListSearch from './ChatListSearch';
import List from './List';
class ChatList extends Component {
render(){
return(
<div className="chatlist">
<ChatListHeader />
<ChatListSearch />
<List contacts={this.props.contacts} getChats={this.props.getChats}/>
</div>
)
}
}
export default ChatList;
import React, { Component } from 'react';
class ChatListSearch extends Component {
render(){
return(
<div className="chatlist--search" style={{"position": "relative"}}>
<input type="" placeholder="search or start a new chat"/>
<img src={require('../../images/searchicon.png')} alt={""} style={{"width": "15px", "position":"absolute","bottom":"18px","left":"20px","fontSize":"15px"}} />
</div>
)
}
}
export default ChatListSearch;
import React, { Component } from 'react';
import ListItem from '../presentational/ListItem';
class List extends Component {
render(){
let listitem = this.props.contacts.map((item) => {
return <ListItem item={item} key={item.id} getChats={ this.props.getChats }/>
})
return(
<div className="list">
{listitem}
</div>
)
}
}
export default List;
import React, { Component } from "react";
class ListItem extends Component {
render(){
let item = this.props.item;
return (
<div className="list--item" onClick={ (event) => { this.props.getChats(event, item.id) } }>
<div className="list--item__image">
<img src={item.profilePhoto} alt={item.name}/>
</div>
<div className="list--item__details">
<h3>{item.name}</h3>
<p>{item.lastMessage}</p>
</div>
</div>
)
}
}
export default ListItem;
import React, { Component } from 'react';
import ViewWhatsapp from '../presentational/ViewWhatsapp';
import ViewChatHistory from './ViewChatHistory';
class View extends Component {
render(){
let visibility = this.props.visibility;
let view;
view = (visibility) ? <ViewChatHistory selectedContactChat={ this.props.selectedContact } /> : <ViewWhatsapp />
return(
<div className="view" style={{"display": "flex", "flexGrow": "1"}}>
{ view }
</div>
)
}
}
export default View;
import React, { Component } from 'react';
class ViewChatHistory extends Component{
render(){
console.log(this.props.selectedContactChat);
return(
<div className="view--chat" >
{
this.props.selectedContactChat.map( (contact) =>{
return (
<header key={contact.id} >
<div className="user-details">
<img src={contact.profilePhoto} style={{"width": "45px", "height": "40px", "borderRadius": "100%", "marginRight": "20px"}} alt={""}/>
<div className="user-details-name">
<h3>{contact.name}</h3>
</div>
</div>
<nav className="extra-icons" style={{"width": "120px", "display": "flex", "justifyContent" : "space-between", "marginTop" : "10px"}}>
<i className="material-icons"></i>
<i className="material-icons"></i>
<i className="material-icons"></i>
</nav>
</header>
)
})
}
</div>
)
}
}
export default ViewChatHistory;
Responsive design is not something you do "differently" in React. You've still got the same old HTML, CSS, JS running in the browser.
Read more about responsive design
I've hit a wall and have no clue what to do. I'm simply trying to close my bootstrap modal but it just won't close. It opens fine but again it just won't close. I've tried so many ways to rectify this that there won't be enough space on this post to list all my attempts.
What am I doing wrong and how can I fix this?
Here's App.js:
import React, { Component } from 'react';
import Product from './Product';
import { ProductConsumer } from "../context";
import TitleBody from "./TitleBody";
import AboutButton from "./AboutButton";
import AboutButtonModal from "./AboutButtonModal";
export default class App extends Component {
constructor(props) {
console.log("Props - ", props);
super(props);
this.state = {
modalVisible: false
};
this.openModal = this.openModal.bind(this);
}
openModal() {
console.log("Open modal called ", this.state.modalVisible);
const modalVisible = !this.state.modalVisible;
this.setState({
modalVisible
}, console.log(this.state.modalVisible));
}
render() {
let styles = this.state.modalVisible
? { display: "block" }
: { display: "none" };
return (
<React.Fragment>
<div className="py-5">
<div className="container">
<TitleBody name="Welcome to" title="Cruskip"/>
<AboutButtonModal show={this.state.modalVisible} onClick={this.openModal} style={styles}/>
<AboutButton onClick={this.openModal}/>
</div>
</div>
</div>
</React.Fragment>
);
}
}
Here's AboutButtonModal.js:
import React from 'react';
import './AboutButtonModal.scss';
const Modal = ({ handleClose, show, children}, props) => {
const showHideClassname = show ? "modal display-block" : "modal display-none";
return(
<div className={showHideClassname} style={props.style}>
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<button
type="button"
onClick={props.onClick}
className="close"
>
×
</button>
<h4 className="modal-title">Modal Header</h4>
</div>
<div className="modal-body">
<p>Some text in the modal.</p>
</div>
<div className="modal-footer">
<button
onClick={props.onClick}
type="button"
className="btn btn-default"
>
Close
</button>
</div>
</div>
</div>
</div>
);
};
export default Modal;
Here's AboutButton.js:
import React from 'react';
import './AboutButton.scss';
const AboutButton = (props) => {
return(
<div className="row">
<button className="about-btn" type="button" onClick={props.onClick}>
About Us
</button>
</div>
);
};
export default AboutButton;
If you are using ({ handleClose, show, children }, props),It won't give you to access of props onClick function..
So to use props, just apply object destructing of props..Now,Your code will become like this...
const Modal = ({ handleClose, show, children,...props})
I am trying to generate a random user information when pressing the button, and display the information above the button. In ProfilePanel.js, I created a avatar and user constants, which will use to show the information. In index.js, the avatar constant works for that since it doesn't need to use the Button. however, for user constant, it doesn't work. In below's code, I am fetching a api data to display user name, but it didn't show anything, I am not sure where wrong, is something wrong in Button.js or index.js. and how can I fix it. Can somebody help me out? Thanks.
<Button title="name" >
<p key={contact.name} user={contact.name}></p>
</Button>
index.js
import React, { Component } from "react";
import ReactDOM from "react-dom";
import Panel from "./ProfilePanel";
import axios from 'axios';
import './index.css';
import Button from './Button';
const url = 'https://randomuser.me/api/';
class App extends Component {
constructor(props) {
super(props);
this.state = {
contacts: []
}
}
componentDidMount() {
this.fetchdata();
}
fetchdata() {
axios.get(url)
.then(res => {
console.log(res);
this.setState({ contacts: res.data.results});
});
}
render(){
const {contacts} = this.state;
return (
<div className="panel">
{contacts.map(contact => (
<div class="panel">
<Panel
key={contact.picture} avatar={contact.picture.medium}
/>
<li class="flex-container">
<Button title="name" >
<p key={contact.name} user={contact.name}></p>
</Button>
<Button title="location" onClick={this.fetchdata}>
</Button>
<Button key={contact.email} title="email">
</Button>
<Button key={contact.phone} title="phone">
</Button>
<Button key={contact.login.password} title="password">
</Button>
</li>
</div>
))}
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById("root")
);
ProfilePanel.js
import React, { Component } from "react";
import PropTypes from "prop-types";
import './index.css';
import Button from './Button';
const style={
borderRadius: 150,
margin: 15,
}
class Panel extends Component {
render() {
const { avatar, user } = this.props;
return (
<div className="Panel">
<div class="panels">
<div className="avatar">
<img src={avatar} class="imageStyle" alt="" width={"200%"} height={"auto"}/>
</div>
</div>
<div class="center">
<h2 className="user">{user}</h2>
</div>
</div>
);
}
}
export default Panel;
Button.js
import './index.css';
import React, { Component } from 'react';
class Button extends Component {
constructor(props) {
super(props);
this.state = {
open:false,
};
}
render() {
const { title } = this.props;
const {open} = this.state;
return (
<button className={` ${open ? 'open' : ''}`}
class='button' onClick={(e) => this.handleClick(e)}>
<div className="panel-heading">
<h2 class='buttoncenter'>{title}</h2>
</div>
</button>
);
}
handleClick(e) {
e.preventDefault();
this.setState({
open: this.state.open
})
}
}
export default Button;
You're not changing state in the handle click. You need to set open to true;
handleClick(e) {
e.preventDefault();
this.setState({
open: true
})
}
You need to pass your user information in index.js. I think you have missed to pass the user props to the panel component, so that it shows the avatar alone. Without passing the users props, you are trying to destructure there in panel component.
//index.js should be like this
render(){
const {contacts} = this.state;
return (
<div className="panel">
{contacts.map(contact => (
<div class="panel">
<Panel
key={contact.picture} user={contact.name} avatar={contact.picture.medium}
/>
<li class="flex-container">
<Button title="name" >
<p key={contact.name} user={contact.name}></p>
</Button>
<Button title="location" onClick={this.fetchdata}>
</Button>
<Button key={contact.email} title="email">
</Button>
<Button key={contact.phone} title="phone">
</Button>
<Button key={contact.login.password} title="password">
</Button>
</li>
</div>
))}
</div>
);
}
New to React/Redux, I am having hard time implementing on event handling.
I know that the 'this' reference key goes null when passed into the map (this.props.addRecipe.map of recipebox) function but I don't how to resolve it.
Essentially I would like to pass the onChange handler to ModalBox for each element in the array.
src/containers/recipebox
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { ListGroup, ListGroupItem, Panel, Button, Modals } from 'react-bootstrap';
import MyModal from '../components/mymodal';
import { bindActionCreators } from 'redux';
import { deleteRecipe } from '../actions/index';
import shortid from 'shortid'
import ModalBox from '../containers/modalbox'
class RecipeBox extends Component {
constructor(props){
super(props);
this.renderRecipeList = this.renderRecipeList.bind(this)
this.handleRecipeNameChange = this.handleRecipeNameChange.bind(this)
this.handleUserIngredientsChange = this.handleUserIngredientsChange.bind(this)
}
handleRecipeNameChange(event){
this.setState({recipeName: event.target.value})
}
handleUserIngredientsChange(event){
this.setState({userIngredients: event.target.value})
}
renderRecipeList(recipeItem, index){
const recipe = recipeItem.recipe;
const ingredients = recipeItem.ingredients;
const id = shortid.generate();
return(
<div key={id}>
<Panel bsStyle="primary" collapsible header={<h3>{recipe}</h3>}>
<ListGroup >
<ListGroupItem header="Ingredients"></ListGroupItem>
{ingredients.map(function(ingredient,index){
return <ListGroupItem key={index}>{ingredient}</ListGroupItem>;
})}
<ListGroupItem>
<Button
onClick={() => this.props.deleteRecipe(recipeItem)}
bsStyle="danger">Delete
</Button>
<ModalBox
modalTextTitle={'Edit Recipe'}
recipeName={recipe}
userIngredients={ingredients}
handleRecipeNameChange={this.handleRecipeNameChange}
handleUserIngredientsChange={this.handleUserIngredientsChange}
onClickSubmit={this.onClickSubmit}
/>
</ListGroupItem>
</ListGroup>
</Panel>
</div>
)
}
render(){
return(
<div className="container">
<div className='panel-group'>
{this.props.addRecipe.map(this.renderRecipeList)}
</div>
</div>
)
}
}
function mapStateToProps(state) {
return {
addRecipe : state.recipeState
};
}
function mapDispatchToProps(dispatch){
return bindActionCreators({deleteRecipe : deleteRecipe}, dispatch)
}
export default connect(mapStateToProps,mapDispatchToProps)(RecipeBox);
src/containers/modalbox
import React, { Component } from 'react';
import { Button, Modal } from 'react-bootstrap';
class ModalBox extends Component {
constructor(props){
super(props)
this.state = {
showModal: false
};
this.toggleModal = this.toggleModal.bind(this);
}
toggleModal(){
this.setState({
showModal: !this.state.showModal
});
}
submitData(link){
link()
this.toggleModal()
}
render() {
return (
<div>
<Button
bsStyle="info"
onClick={this.toggleModal}
>
{this.props.modalTextTitle}
</Button>
<Modal show={this.state.showModal} onHide={this.toggleModal}>
<Modal.Header closeButton>
<Modal.Title>{this.props.modalTextTitle}</Modal.Title>
</Modal.Header>
<Modal.Body>
<form>
<div className="form-group">
<label htmlFor="recipeName">Name of Recipe:</label>
<input
value={this.props.recipeName}
onChange= {this.props.handleRecipeNameChange}
type="text"
className="form-control"
id="recipeName" />
</div>
<div className="form-group">
<label htmlFor="userIngredients">Ingredients:</label>
<textarea
placeholder="you can seperate by comma"
value={this.props.userIngredients}
onChange={this.props.handleUserIngredientsChange}
type="text"
className="form-control"
id="userIngredients" />
</div>
</form>
</Modal.Body>
<Modal.Footer>
<Button
bsStyle="info"
onClick={ () => this.submitData(this.props.onClickSubmit) }>
{this.props.modalTextTitle}
</Button>
<Button
bsStyle="danger"
onClick= {this.toggleModal}
>Close</Button>
</Modal.Footer>
</Modal>
</div>
);
}
}
export default ModalBox
inside map function you need to change the this like below code,
render(){
const self = this;
return(
<div className="container">
<div className='panel-group'>
{this.props.addRecipe.map(self.renderRecipeList)}
</div>
</div>
)
}