How to setState onclick for two components in React? - javascript

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.

Related

React onClick event button - class component

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

onClick of button triggering all the components to open - Reactjs

I implemented a Card component and basically generating a bunch of cards on some input data. I binded a setter function on button click on every card which basically expands and collapse it. Even after putting unique keys to the div is sort of triggering all the cards to open at once.
Here is the code piece:
import React, { useState } from 'react';
import PrettyPrintJson from './PrettyPrintJson';
import './Card.scss';
import '../App.scss';
const Card = (props) => {
const { data } = props;
const [collapse, toggleCollapse] = useState(true);
return (<div className="card-group">
{data.map((obj, idx)=>{
return <div className="card" key={`${idx}_${obj?.lastModifiedOn}`}>
<div className="card-header">
<h4 className="card-title">{`fId: ${obj?.fId}`}</h4>
<h6 className="card-title">{`name: ${obj?.name}`}</h6>
<h6 className="card-title">{`status: ${obj?.status}`}</h6>
<div className="heading-elements">
<button className="btn btn-primary" onClick={() => toggleCollapse(!collapse)}>Show Json</button>
</div>
</div>
<div className={`card-content ${!collapse ? 'collapse show' : 'collapsing'}`}>
<div className="card-body">
<div className="row">
<PrettyPrintJson data={ obj } />
</div>
</div>
</div>
</div>
})}
</div>
);
}
export default Card;
Create a component that manages it's own state and render that component.
const CardItem = ({ obj }) => {
const [collapse, toggleCollapse] = useState(true);
return (<div className="card">
<div className="card-header">
<h4 className="card-title">{`fId: ${obj?.fId}`}</h4>
<h6 className="card-title">{`name: ${obj?.name}`}</h6>
<h6 className="card-title">{`status: ${obj?.status}`}</h6>
<div className="heading-elements">
<button className="btn btn-primary" onClick={() => toggleCollapse(!collapse)}>Show Json</button>
</div>
</div>
<div className={`card-content ${!collapse ? 'collapse show' : 'collapsing'}`}>
<div className="card-body">
<div className="row">
<PrettyPrintJson data={ obj } />
</div>
</div>
</div>
</div>)
}
then render it like
{data.map((obj, idx)=> (<CardItem obj={obj} key={idx} />))}
I think you can declare a state which is a type of int. After then, you can use the if-statement of index(idx) and state.
Like this:
const [collapsedCardNumbers, toggleCollapseCard] = useState([]);
const addCardNumber = (idx, prevState) => {
const arr_cardNum = prevState
!arr_cardNum .includes(idx) && arr_cardNum .push(idx)
return arr_cardNum
}
...
{data.map((obj, idx)=>{
return <div className="card" key={`${idx}_${obj?.lastModifiedOn}`}>
<div className="card-header">
<h4 className="card-title">{`fId: ${obj?.fId}`}</h4>
<h6 className="card-title">{`name: ${obj?.name}`}</h6>
<h6 className="card-title">{`status: ${obj?.status}`}</h6>
<div className="heading-elements">
<button className="btn btn-primary" onClick={() => toggleCollapseCard(prevState => addCardNumber(idx, prevState))}>Show Json</button>
</div>
</div>
<div className={`card-content ${collapsedCardNumbers.includes(idx) ? 'collapse show' : 'collapsing'}`}>
<div className="card-body">
<div className="row">
<PrettyPrintJson data={ obj } />
</div>
</div>
</div>
</div>
})}

React How to show individual data into popup

I am learning react I want to show movie data when clicking on particular div. currently, I called fancy box which is not right method to get the result
So I need help to show movie data once click on particular div.
class App extends React.Component {
constructor() {
super();
this.state = {
data: [],
search: '',
};
}
updateSearch(event) {
this.setState({search: event.target.value.substr(0, 20)});
}
componentDidMount() {
fetch('http://www.omdbapi.com/?apikey=MyKey&s=fast&plot=full')
.then((Response) => Response.json())
.then((findresponse) => {
console.log(findresponse);
this.setState({
data: findresponse.Search,
});
});
}
render() {
let filteredMovie = this.state.data.filter((dynamicData) => {
return dynamicData.Title.toLowerCase().indexOf(this.state.search.toLowerCase()) !== -1;
});
return (
<div className="container movies_list">
<div className="row">
<div className="col-md-12 p-4">
<form>
<input
type="text"
className="form-control"
placeholder="Search"
value={this.state.search}
onChange={this.updateSearch.bind(this)}
/>
</form>
</div>
{filteredMovie &&
filteredMovie.map((dynamicData, key) => (
<div className="col-md-3 mb-3" key={key}>
<div className="card">
<img src={dynamicData.Poster} className="card-img-top" alt="..." />
<div className="card-body">
<h6 className="card-title">{dynamicData.Title} </h6>
<h6 className="card-title">Year: {dynamicData.Year} </h6>
<p className="card-text">{dynamicData.Plot} </p>
<a
data-fancybox
data-src="#hidden-content"
href="javascript:;"
className="btn btn-info"
>
View
</a>
<div id="hidden-content">
<img src={dynamicData.Poster} className="card-img-top" alt="..." />
<h2>{dynamicData.Title}</h2>
<p>{dynamicData.Year}</p>
</div>
</div>
</div>
</div>
))}
</div>
</div>
);
}
}
I highly recommend Reakit for modal & popovers.

My component 'Recipe Items' is being rendered in a single column, when the correct one for each row is 5 columns

I have to render a component from an .json file, until then okay, to be able to read and pass the api values ​​to my component ('RecipeItem'). The problem lies in the part of rendering, because the correct one would be the components being in 5 columns instead of only one.
enter image description here
updated codes below !!!
File RecipeItem.js
const RecipeList = ({ searchString }) => {
return(
<div>
{console.log('to aqui')}
<img className="card-img-top img-fluid" src={searchString.thumbnail} alt={searchString.title} />
<div className="card-body">
<h5 className="card-title">{searchString.title}</h5>
<p className="card-text">
<strong>Ingredients: </strong>{searchString.ingredients}
</p>
</div>
</div>
)
}
const RecipeItem = (props) => {
return (
<div className="col-sm-3 mt-4">
<div className="card">
{props.list && props.list.map((searchString, index) =>
<RecipeList searchString={searchString} key={index} />
)}
</div>
</div>
)
}
File App.js
class App extends Component {
constructor(props) {
super(props);
this.state = {
searchString: []
};
}
componentDidMount() {
this.setState({ searchString : data.results })
}
render() {
return (
<div className="App">
<Navbar />
<div className="container mt-10">
<div className="row">
<RecipeItem list={this.state.searchString}/>
</div>
</div>
</div>
);
}
}
Is this working ?
class App extends Component {
render() {
return (
<div className="App">
<Navbar />
<div className="container mt-10">
<div className="row">
{RecipesData.results.map(recipe =>
<RecipeItem
title={recipe.title}
ingredients={recipe.ingredients}
source={recipe.href}
thumbnail={recipe.thumbnail} />
)}
</div>
</div>
</div>
);
}
}

Show infinite scrolling with loader

I am learning reactjs by creating a movie listing app. I have created a movie list using reactjs, react-mdl. I could not use infinite scrolling feature. For example at first, 3 movies are shown on the list and when user scrolls down more 3 movies list should be shown but before showing further 3 movies in list a loader icon should be shown. How can i show such on this scenario of code? I know i have to define the state isLoading and isLoadingMore. If the state of isLoading is false loader should be hidden else a loader should be shown.
App.js
class App extends Component {
constructor(props){
super(props);
this.state = {
movie:[
{
title:'The Flash',
id:1,
imageUrl:'http://cdn3-www.superherohype.com/assets/uploads/gallery/the-flash-2x15/12771634_1000420230050280_1499915054664837033_o.jpg',
rottenTomatoes:'85%'
},
{
title:'Silicon Valley',
id:2,
imageUrl:'https://www.wired.com/wp-content/uploads/2016/03/silicon_valley_opener4.jpg',
rottenTomatoes:'90%'
},
{
title:'Scorpion',
id:3,
imageUrl:'http://cdn-uploads.gameblog.fr/images/blogs/36115/202746.jpg',
rottenTomatoes:'80%'
},
],
isLoading:true,
isLoadingMore:false
}
}
showLoaderIcon(){
this.setState({
isLoading:true
});
}
hideLoaderIcon(){
this.setState({
isLoading:false
});
}
render() {
return (
<div className="App">
<div className="demo-big-content">
<Layout fixedHeader>
<Header>
<HeaderRow title="Find TV Shows">
</HeaderRow>
<HeaderRow>
<div className="search">
<span className="material-icons search-icon">search</span>
<input type="text" className="form-control" placeholder="Name of show(e.g Friends)" />
</div>
</HeaderRow>
</Header>
<Drawer title="Title">
</Drawer>
<Content>
<div className="page-content" />
<MovieList movies={this.state.movie} />
</Content>
</Layout>
</div>
</div>
);
}
}
export default App;
MovieListItem.js
const MovieListItem = ({movie}) => {
const imageUrl = movie.imageUrl;
const mainCast = _.map(movie.mainCast, (cast) => {
return(
<li className="list-item">
{cast}
</li>
)
})
return (<li className="list-group-item">
<div className="video-list media">
<div className="media-left">
<img className="media-object" src={imageUrl} alt={movie.title} />
</div>
<div className="media-body">
<div className="media-heading">
<h4 className="title">{movie.title}</h4>
</div>
<div className="main-cast">
<ul id="cast-list">
{mainCast}
</ul>
</div>
<div className="reviewer">
<div className="col-sm-3 col-xs-3">
<img src={imdb} className="reviewer-img img-responsive" alt="imdb" />
</div>
<div className="col-sm-3 col-xs-3">
<p className="reviewer-score">{movie.imdb}</p>
</div>
<div className="col-sm-3 col-xs-3">
<img src={rottenTomatoes} className="reviewer-img img-responsive" alt="rottenTomatoes"/>
</div>
<div className="col-sm-3 col-xs-3">
<p className="reviewer-score">{movie.rottenTomatoes}</p>
</div>
</div>
</div>
</div>
</li>
)
};
export default MovieListItem;

Categories