happy 2022!
I'm working on adding an onClick event listener/button.
This app it's an Airbnb clone, and I'm working on the SingleRoom page, which displays details about that specific room/home.
Once the user clicks to a particular place, it will redirect to the SingleRoom page (below code) it has a "Reserve now" button so the user can pay for the room/home.
When I try adding the button, I get this error:
Uncaught Error: SingleRoom(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
The reserve button will open a modal to either cancel or continue to make the payment.
Would you please help me in making the code work?
Thank you
Updated the code.. It now has the else statement
But it throws this error:
Line 29:15: 'setOpenModal' is not defined no-undef
import React, { Component } from "react";
import defaultBcg from "../images/room-1.jpeg";
import Banner from "../components/Banner";
import { Link } from "react-router-dom";
import { RoomContext } from "../context";
import StyledHero from "../components/StyledHero";
import "../components/Modal.css";
export default class SingleRoom extends Component {
constructor(props) {
super(props);
this.state = { slug: this.props.match.params.slug, defaultBcg };
}
static contextType = RoomContext;
render() {
const { getRoom } = this.context;
const room = getRoom(this.state.slug);
if (!room) {
return (
<div className="error">
<h3>Sorry we couldn't find the room you were looking for'...</h3>
<Link to="/rooms" className="btn-primary">
Back to rooms
</Link>
</div>
);
}else{
Modal(setOpenModal);
}
function Modal({ setOpenModal }) {
const {
name,
description,
capacity,
size,
price,
extras,
breakfast,
pets,
images,
} = room;
const [mainImg, ...defaultImg] = images;
return (
<>
<StyledHero img={mainImg || this.state.defaultBcg}>
<Banner title={`${name}`}></Banner>
</StyledHero>
<section className="single-room">
<div className="single-room-images">
{defaultImg.map((item, index) => {
return <img key={index} src={item} alt={name} />;
})}
</div>
<div className="single-room-info">
<article className="desc">
<h3>details</h3>
<p>{description}</p>
</article>
<article className="info">
<h3>Info</h3>
<h6>Price: ${price}</h6>
<h6>Size: ${size} SQFT</h6>
<h6>
Max capacity: {""}
{capacity > 1 ? `${capacity} people` : `${capacity} person`}
</h6>
<h6>{pets ? "pets allowed" : "no pets allowed"}</h6>
<h6>{breakfast && "free breakfast included"}</h6>
<div className="modalBackground">
<div className="modalContainer">
<div className="titleCloseBtn">
<button
onClick={() => {
setOpenModal(false);
}}
>
X
</button>
</div>
<div className="title">
<h1>Reservation details</h1>
</div>
<div className="body">
<p>
Total amount {{price}}
</p>
</div>
<div className="footer">
<button
onClick={() => {
setOpenModal(false);
}}
id="cancelBtn"
>
Cancel
</button>
<button>Reserve Now</button>
</div>
</div>
</div>
</article>
</div>
</section>
<section className="room-extras">
<h6>extras</h6>
<ul className="extras">
{extras.map((item, index) => {
return <li key={index}>- {item}</li>;
})}
</ul>
</section>
</>
);
}
}
}
Because there is no else condition found. Change your code to
render() {
const { getRoom } = this.context;
const room = getRoom(this.state.slug);
if (!room) {
return (
<div className="error">
<h3>Sorry we couldn't find the room you were looking for'...</h3>
<Link to="/rooms" className="btn-primary">
Back to rooms
</Link>
</div>
);
}else{
Modal(setOpenModal);
}
}
function Modal({ setOpenModal }) {
const {
name,
description,
capacity,
size,
price,
extras,
breakfast,
pets,
images,
} = room;
const [mainImg, ...defaultImg] = images;
return (
<>
<StyledHero img={mainImg || this.state.defaultBcg}>
<Banner title={`${name}`}></Banner>
</StyledHero>
<section className="single-room">
<div className="single-room-images">
{defaultImg.map((item, index) => {
return <img key={index} src={item} alt={name} />;
})}
</div>
<div className="single-room-info">
<article className="desc">
<h3>details</h3>
<p>{description}</p>
</article>
<article className="info">
<h3>Info</h3>
<h6>Price: ${price}</h6>
<h6>Size: ${size} SQFT</h6>
<h6>
Max capacity: {""}
{capacity > 1 ? `${capacity} people` : `${capacity} person`}
</h6>
<h6>{pets ? "pets allowed" : "no pets allowed"}</h6>
<h6>{breakfast && "free breakfast included"}</h6>
<div className="modalBackground">
<div className="modalContainer">
<div className="titleCloseBtn">
<button
onClick={() => {
setOpenModal(false);
}}
>
X
</button>
</div>
<div className="title">
<h1>Reservation details</h1>
</div>
<div className="body">
<p>
Total amount {{price}}
</p>
</div>
<div className="footer">
<button
onClick={() => {
setOpenModal(false);
}}
id="cancelBtn"
>
Cancel
</button>
<button>Continue</button>
</div>
</div>
</div>
</article>
</div>
</section>
<section className="room-extras">
<h6>extras</h6>
<ul className="extras">
{extras.map((item, index) => {
return <li key={index}>- {item}</li>;
})}
</ul>
</section>
</>
);
}
Related
I have created an ecommerce site. Within my Product.js I have an onclick function for each product that pushes the product to local storage and updates the state of the shopping cart.
However, my cart.js contains the totals like total products, taxes, total amount...etc.
How can I add setState to get them to update when a product is added? I tried adding setState within the return section of the Cart.js but that ended up creating an endless loop of error messages. Below is the code:
import React,{Component} from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import {faCartPlus} from "#fortawesome/free-solid-svg-icons";
class Products extends Component {
constructor(props, context) {
super(props)
this.state={
shoppingCart:[]
}
}
addToCart=(item)=>{
this.state.shoppingCart.push(item)
this.setState({shoppingCart:this.state.shoppingCart})
localStorage.setItem('cart',JSON.stringify(this.state.shoppingCart))
}
render() {
return (
<div className="container prod-cntr">
<div className="row prod-row">
{this.props.products?.map((element) => (
<div className="col-lg-3 prod-col" key={element.id}>
<div className="card card-container">
<img
src={element.image}
alt="product img"
className="prod-img"
/>
<div className="card-body">
<p className="card-title">{element.product}</p>
<p className="card-text">{element.description}</p>
<p className="prod-price">{element.price} <FontAwesomeIcon icon={faCartPlus} className="prod-carticon" onClick={()=>{this.addToCart(element)}} /></p>
</div>
</div>
</div>
))}
</div>
<div>
</div>
</div>
);
}
}
export default Products;
import React, { Component } from "react";
import plus from "./assets/images/plus.svg";
import minus from "./assets/images/minus.svg";
class Cart extends Component{
constructor(props){
super(props)
this.state = {
totalItems: 0,
amount:0,
taxes: 0,
totalAmount: 0
}
}
render(){
const cartItems = JSON.parse( localStorage.getItem('cart'));
const totalItems = cartItems?.length || 0;
const amount = cartItems?.reduce((accumulator, object) => {
return accumulator + object.price;},0) ||0;
const taxes = (amount * 0.065);
const totalAmount = amount + taxes;
return(<>
<div>
<h2>YOUR CART</h2>
<p>Total Items <span>{this.state.totalItems} </span></p>
<p>Amount <span>{this.state.amount}</span></p>
<p>Total Taxes <span>{this.state.taxes}</span></p>
<p>Total Amount <span>{this.state.totalAmount}</span></p>
<p>Check Out</p>
</div>
<div className="container prod-cntr">
<div className="row prod-row">
{cartItems?.map((element) => (
<div className="col-lg-3 prod-col" key={element.id}>
<div className="card card-container">
<img
src={element.image}
alt="product img"
className="prod-img"
/>
<div className="card-body">
<p className="card-title">{element.product}</p>
<p className="card-text">{element.description}</p>
<div className = "quantity-container">
<img src={minus} className ="minus"/> <p className ="quantity" >QUANTITIY:<span className="qnty-txt"></span></p> <img src={plus} className ="plus"/>
</div>
<button onClick={localStorage.removeItem("item")}>Remove From Cart</button>
</div>
</div>
</div>
))}
</div>
<div>
</div>
</div>
</>)
}
}
export default Cart;
you have to create createRef in class component, as you can see the below code I'm using createRef, You have to push the values in this.items and then you have to set the this.items in setState. I hope this would be helpful.
class Products extends Component {
constructor(props, context) {
super(props)
this.state={
}
this.items = React.createRef([]);
}
addToCart=(item)=>{
this.items.current.push(item);
localStorage.setItem('cart',JSON.stringify(this.items.current))
}
render() {
return (
<div className="container prod-cntr">
<div className="row prod-row">
{this.props.products?.map((element) => (
<div className="col-lg-3 prod-col" key={element.id}>
<div className="card card-container">
<img
src={element.image}
alt="product img"
className="prod-img"
/>
<div className="card-body">
<p className="card-title">{element.product}</p>
<p className="card-text">{element.description}</p>
<p className="prod-price">{element.price} <FontAwesomeIcon icon={faCartPlus} className="prod-carticon" onClick={()=>{this.addToCart(element)}} /></p>
</div>
</div>
</div>
))}
</div>
<div>
</div>
</div>
);
}
}
export default Products;
here second method:
let items = [];
class Products extends Component {
constructor(props, context) {
super(props)
this.state={
}
}
componentDidMount() {
items = [];
}
addToCart=(item)=>{
items.push(item);
console.log(items)
localStorage.setItem('cart',JSON.stringify(items))
}
render() {
return (
<div className="container prod-cntr">
<div className="row prod-row">
{this.props.products?.map((element) => (
<div className="col-lg-3 prod-col" key={element.id}>
<div className="card card-container">
<img
src={element.image}
alt="product img"
className="prod-img"
/>
<div className="card-body">
<p className="card-title">{element.product}</p>
<p className="card-text">{element.description}</p>
<p className="prod-price">{element.price} <FontAwesomeIcon icon={faCartPlus} className="prod-carticon" onClick={()=>{this.addToCart(element)}} /></p>
</div>
</div>
</div>
))}
</div>
<div>
</div>
</div>
);
}
}
export default Products;
Cart.js
import React, {Component} from 'react';
import plus from './assets/images/plus.svg';
import minus from './assets/images/minus.svg';
class Cart extends Component {
constructor(props) {
super(props);
this.state = {
totalItems: 0,
amount: 0,
taxes: 0,
totalAmount: 0
};
}
removeItem = (id) => {
const cartItems = JSON.parse(localStorage.getItem('cart'));
const filter = cartItems.filter(item => item.id !== id);
if (filter) {
JSON.stringify(localStorage.setItem('cart', filter));
}
}
render() {
const cartItems = JSON.parse(localStorage.getItem('cart'));
const totalItems = cartItems?.length || 0;
const amount =
cartItems?.reduce((accumulator, object) => {
return accumulator + object.price;
}, 0) || 0;
const taxes = amount * 0.065;
const totalAmount = amount + taxes;
return (
<>
<div>
<h2>YOUR CART</h2>
<p>
Total Items <span>{this.state.totalItems} </span>
</p>
<p>
Amount <span>{this.state.amount}</span>
</p>
<p>
Total Taxes <span>{this.state.taxes}</span>
</p>
<p>
Total Amount <span>{this.state.totalAmount}</span>
</p>
<p>Check Out</p>
</div>
<div className="container prod-cntr">
<div className="row prod-row">
{cartItems?.map(element => (
<div className="col-lg-3 prod-col" key={element.id}>
<div className="card card-container">
<img src={element.image} alt="product img" className="prod-img" />
<div className="card-body">
<p className="card-title">{element.product}</p>
<p className="card-text">{element.description}</p>
<div className="quantity-container">
<img src={minus} className="minus" />{' '}
<p className="quantity">
QUANTITIY:<span className="qnty-txt"></span>
</p>{' '}
<img src={plus} className="plus" />
</div>
<button onClick={() => this.removeItem(element.id)}>Remove From Cart</button>
</div>
</div>
</div>
))}
</div>
<div></div>
</div>
</>
);
}
}
export default Cart;
re-rendering the components after the button click will solve your problem.
I have done one sample example for your type problem here:
https://codesandbox.io/s/stateupdatetest-pb811e
if you find any difficulties regarding the solution. Please reply on this thread.
I have list of items = exemple1, exemple2, ..... and each one clicked
when I choose one of the list the URL be like :
localhost:3000/portfolio/exemple1
localhost:3000/portfolio/exemple2
how I can do it please?
the list
import { Link } from 'react-router-dom';
export const ListPortfolio = (props) => {
const portfolio = props.portfolio;
const number = props.number;
return(
<div className="d-flex flex-wrap table">
{portfolio.map((item, i) =>
<Link to={{ pathname:"/DetailPortfolio", state:item}} state className="text-black text-decoration-none link" key={item.id} >
<div className="card">
<img src={item.image} alt={item.title} />
<div className="card-body">
<h3 className="card-title">{item.title}</h3>
<span className="url">URL: </span><span>{item.excerpt && item.excerpt.slice(10, -5)}</span>
</div>
</div>
</Link>
)}
</div>
)
}
detailsOfList
const DetailPortfolio = (props) => {
const {state} = props.location
return (
<>
<div className="container-fluid">
<Details>
<div>
<div>{state.title}</div>
<img src={state.image} alt={state.title} />
<div>{state.content}<div/>
</div>
</div>
</Details>
</div>
</>
);
}
when i add product in cart there is nothing displayed in car and "items not found" error is displayed on the screen and when i try to use null check and npm start then error is not shown but still balnk page is displayed.
import React from 'react';
import Header from './Front/Header/Header';
const Cart = ({ cartitems ,handleAddProduct,handleRemoveProduct} ) => {
return (
<>
<Header />
<div className="cart-items">
<div className="cart-items-header"> cartitems</div>
{!cartitems?.length ? (
<div className="cart-items-empty"> No items added in cart</div>
) : null}
<div>
{cartitems?.length ? cartitems.map((item,name ,price ,image ,id) => (
<img
key={item.id}
className="cart-items-image"
src={item.image}
alt={item.name}
/>
)) : null}
</div>
<div>
<h3 className='cart-items-name'>
{item.name}</h3>
</div>
<div className='cart-items-function'>
<button className='cart-items-add' onClick={() =>handleAddProduct(item)}>
+
</button>
<button
className='cart-items-remove' onClick={()=>handleRemoveProduct(item)}>
-
</button>
</div>
<div
className='cart-items-price'>
{item.quantity}* ${item.price}
</div>
</div>
</>
);
}
export default Cart;
I think there are 2 problems in your code :
Firstly, I suppose that each item in your cartitems has the following structure :
item = {
id: 1,
name: "my item",
price: 2,
image: "http://mybeautifulurl.org/image.png"
}
If that is the case, you should pass item as the only argument in the map function, and access each props with item.id, item.name, item.price and item.image.
Some of your code is not in the map function but needs the item, so it cannot display anything.
Here is your updated code :
import React from "react";
import Header from "./Front/Header/Header";
const Cart = ({ cartitems, handleAddProduct, handleRemoveProduct }) => {
return (
<>
<Header />
<div className="cart-items">
<div className="cart-items-header"> cartitems</div>
{!cartitems?.length ? (
<div className="cart-items-empty"> No items added in cart</div>
) : null}
<div>
{cartitems?.length
? cartitems.map((item) => (
<>
<img
key={item.id}
className="cart-items-image"
src={item.image}
alt={item.name}
/>
<div>
<h3 className="cart-items-name">{item.name}</h3>
</div>
<div className="cart-items-function">
<button
className="cart-items-add"
onClick={() => handleAddProduct(item)}
>
+
</button>
<button
className="cart-items-remove"
onClick={() => handleRemoveProduct(item)}
>
-
</button>
</div>
<div className="cart-items-price">
{item.quantity}* ${item.price}
</div>
</>
))
: null}
</div>
</div>
</>
);
};
export default Cart;
Let me know if this solves the problem :)
*I am currently trying to figure out how to change the background image of my book--container--fiction. I have three different images and depending on the genre of the book I would like to change these images but I am unsure on how to do that. Any help would be great :) I presume it would be similar to doing an if statement. Im just not sure how to go about it
if(book.genre === "fiction") {
return <div className="book--container--fiction"></div>
} else if (book.genre === "non-fiction"){
return <div className="book--container--non-fiction"></div>
} else{
return <div className="book--container--children"></div>
}
*
import React from "react";
import "../stylesheets/Book.scss";
import { FiHeart } from "react-icons/fi";
import { Link } from "react-router-dom";
import isBookInWishlist from "../helper/isBookInWishList";
function Book({ book, handleSelectBook, handleSelectWishList, wishlist }) {
return (
<React.Fragment>
<div className="book--container--fiction">
<div className="book--wrapper">
<div className="book--image">
<img
src={book.url}
alt={book.title}
className="book--image-style"
/>
</div>
<div className="book--content--wrapper">
<div className="book--text--information">
<h3>
{book.title}
<button
onClick={() => handleSelectWishList(book)}
className={
isBookInWishlist(book, wishlist)
? "wishlist--icon orange"
: "wishlist--icon"
}
>
<FiHeart />
</button>
</h3>
<p>{book.author}</p>
<p>{book.genre}</p>
{/* <p>{book.desc}</p> */}
<p className="book--price--style"> € {book.price}</p>
</div>
<Link
to="/productdetails"
className="view--book--button"
onClick={() => handleSelectBook(book)}
>
View Book
</Link>
</div>
</div>
</div>
</React.Fragment>
);
}
export default Book;
You can make use of template literals in JS like so:-
import React from "react";
import "../stylesheets/Book.scss";
import { FiHeart } from "react-icons/fi";
import { Link } from "react-router-dom";
import isBookInWishlist from "../helper/isBookInWishList";
function Book({ book, handleSelectBook, handleSelectWishList, wishlist }) {
return (
<React.Fragment>
<div className={`book--container--${book.genre}`}>
<div className="book--wrapper">
<div className="book--image">
<img
src={book.url}
alt={book.title}
className="book--image-style"
/>
</div>
<div className="book--content--wrapper">
<div className="book--text--information">
<h3>
{book.title}
<button
onClick={() => handleSelectWishList(book)}
className={
isBookInWishlist(book, wishlist)
? "wishlist--icon orange"
: "wishlist--icon"
}
>
<FiHeart />
</button>
</h3>
<p>{book.author}</p>
<p>{book.genre}</p>
{/* <p>{book.desc}</p> */}
<p className="book--price--style"> € {book.price}</p>
</div>
<Link
to="/productdetails"
className="view--book--button"
onClick={() => handleSelectBook(book)}
>
View Book
</Link>
</div>
</div>
</div>
</React.Fragment>
);
}
export default Book;
I'm mapping all of my files
_renderItems = files =>
files
? files.map((item, i) => {
return <ProjectItemUser {...item} key={i} index={i} />;
})
: null;
and then I'm trying to display it ProjectItemUser
class ProjectItemUser extends Component {
render() {
return (
<div>
<div className="book_item">
<div className="book_header">
<h2>{this.props.name}</h2>
</div>
<div className="book_this">
<div className="book_author">{this.props.subject}</div>
<div className="book_bubble">
<strong>Study: </strong> {this.props.study}
</div>
<div className="book_bubble">
<strong>Grade: </strong> {this.props.grade}
</div>
<FontAwesomeIcon icon="trash" id="trash" />
</div>
</div>
</div>
);
}
}
This basically displays all the files, and each file is its own separate row. I would like to assign value to div element on each iteration, so I can control which file has been clicked.
I can access my id with: this.props._id
Should this be done using refs and if so, how ?
You should pass onClick function as parameter
_renderItems = files =>
files
? files.map((item, i) => {
return <ProjectItemUser {...item} key={i} index={i} onClick={() => { console.warn(item) } />;
})
: null;
class ProjectItemUser extends Component {
render() {
return (
<div>
<div className="book_item">
<div className="book_header">
<h2>{this.props.name}</h2>
</div>
<div className="book_this">
<div className="book_author">{this.props.subject}</div>
<div className="book_bubble">
<strong>Study: </strong> {this.props.study}
</div>
<div className="book_bubble">
<strong>Grade: </strong> {this.props.grade}
</div>
<FontAwesomeIcon icon="trash" id="trash" />
<Button onClick={this.props.onClick} label="Click on me" />
</div>
</div>
</div>
);
}
}