Add element in array by button click - reactjs

I have product cards that are rendered based on a json file.
By clicking on the "Add to Cart" button, the element should be added to the array сartList, but this does not happen.
I also tried to forward the function to the component itself, but it didn’t work out too well for me.
Shop.jsx:
import React, { useState } from 'react';
import './Instruments.css';
import Cart from '../components/Cart'
import Product from '../components/Product'
import cart from '../img/cart.png';
import data from "../data/data.json";
unction Shop() {
const [value, setValue] = useState('');
const [currentData, setCurrentData] = useState(data);
const [cartList, setCartList] = useState([]);
return (
<div className='shop'>
<div className='container'>
<div className='shop__main-products'>
{
currentData.filter((el) => {
return value.toLowerCase() === '' ? el : el.title.toLowerCase().includes(value.toLowerCase())
}).map((el, index) => {
return (
<Product img={el.img} title={el.title} price={el.price} key={el.id} onClick={() => setCartList([...cartList, el])}/>
)
})
}
</div>
</div>
<Cart active={modalActive} setActive={modalSetActive}/>
</div>
</div>
);
}
export default Shop;
Product.jsx:
import React, { useState } from 'react';
import './Product.css';
function Product({img, title, price, id, type}) {
return (
<div className='product' key={id} type={type}>
<div className='buy__top'>
<div className='product__top-image-background'>
<img className='product__top-image' src={img}></img>
</div>
<h3 className='product__top-title'>{title}</h3>
</div>
<div className='product__buy'>
<h3 className='product__buy-price'>{price} грн</h3>
<button className='product__buy-button'>В корзину</button>
</div>
</div>
)
}
export default Product;

It looks like the issue is with how you're passing the onClick function to the Product component. The onClick prop should be passed to the "Add to Cart" button, not the Product component itself. You should change the following line:
<Product img={el.img} title={el.title} price={el.price} key={el.id} addToCart={() => setCartList([...cartList, el])}/>
And in the Product component, you should add the onClick prop to the "Add to Cart" button:
<button className='product__buy-button' onClick={addToCart}>В корзину</button>
This way, when the button is clicked, it will call the addToCart function and add the element to the cartList array.

You are not adding the onClick function to the props of the Product component pass it down the pipe and itll work.
function Product({img, title, price, id, type, onClick}) {
return (
<div className='product' key={id} type={type}>
<div className='buy__top'>
<div className='product__top-image-background'>
<img className='product__top-image' src={img}></img>
</div>
<h3 className='product__top-title'>{title}</h3>
</div>
<div className='product__buy'>
<h3 className='product__buy-price'>{price} грн</h3>
<button className='product__buy-button' onClick={onClick}>В корзину</button>
</div>
</div>
)
}

Related

Uncaught TypeError: Cannot read properties of undefined (reading 'img') at Basket.jsx:19:1

The application has the function of adding products to the basket. On the product page it is possible to choose the color and size, if they are not selected in the basket, they will not be marked. But if you select at least one of the colors or sizes, then when you go to the cart page, an Uncaught TypeError: Cannot read properties of undefined (reading 'img') error appears.
But if you click on the selection more than once when choosing a color or size, then everything is displayed correctly on the basket page. As I understand it, the problem lies in the asynchrony of useState, if not difficult, tell me what can be done in this case, thanks!
The application has the function of adding products to the basket. On the product page it is possible to choose the color and size, if they are not selected in the basket, they will not be marked. But if you select at least one of the colors or sizes, then when you go to the cart page, an Uncaught TypeError: Cannot read properties of undefined (reading 'img') error appears.
But if you click on the selection more than once when choosing a color or size, then everything is displayed correctly on the basket page. As I understand it, the problem lies in the asynchrony of useState, if not difficult, tell me what can be done in this case, thanks!
ProductPage
import s from "./ProductPage.module.scss";
import { useParams, NavLink } from "react-router-dom";
import { useEffect, useState } from "react";
import Prices from "./prices/Prices";
import ChooeseSize from "./choosesize/ChooeseSize";
import ChooeseColor from "./choosecolor/ChooeseColor";
import RelatedItems from "./relatedItems/RelatedItems";
const ProductPage = ({ buttonHandler, sizef, colorf, clr, sz }) => {
let { id } = useParams();
let [fetchedData, updateFetchedData] = useState([]);
let { name, price, old_price, img, size, color, related_products } =
fetchedData;
let api = `[some api]`;
useEffect(() => {
(async function () {
let data = await fetch(api).then((res) => res.json());
updateFetchedData(data);
})();
}, [api]);
return (
<div className={s.productpage}>
<div className={s.name}>{name}</div>
<div className={s.path}>
<NavLink to={"/"}>Главная</NavLink> -{" "}
<NavLink to={"/shop"}>Магазин </NavLink> - <span>{name}</span>
</div>
<div className={s.product}>
<img src={img} alt="" />
<div className={s.right}>
<Prices price={price} old_price={old_price} context="page" />
<ChooeseSize size={size} sizef={sizef} />
<ChooeseColor color={color} colorf={colorf} />
<div className={s.buttons}>
<button
onClick={(e) =>
buttonHandler({
id: id,
img: img,
name: name,
size: sz,
color: clr,
price: price,
})
}
className={s.btnopenshop}
>
Добавить в корзину
</button>
</div>
</div>
</div>
<RelatedItems related_products={related_products} />
</div>
);
};
export default ProductPage;
Basket
import s from "./Basket.module.scss";
import BasketItem from "./BasketItem/BasketItem";
import { NavLink } from "react-router-dom";
const Basket = ({ basket }) => {
return (
<div className={s.basket}>
<div className={s.heading}>Корзина</div>
<div className={s.path}>
<NavLink to={"/"}>Главная</NavLink> -{" "}
<NavLink to={"/basket"}>
<span> Корзина</span>
</NavLink>
</div>
{basket?.map((basket) => (
<BasketItem
key={basket.id}
img={basket.img}
name={basket.name}
price={basket.price}
size={basket.size}
color={basket.color}
/>
))}
<div className={s.checkoutbtn}>
<button className={s.checkout}>Оформить заказ</button>
</div>
</div>
);
};
export default Basket;
BasketItem
import s from "./BasketItem.module.scss";
import axios from "axios";
import { useEffect, useState } from "react";
const BasketItem = ({ img, name, price, size, color }) => {
return (
<div className={s.basketitem}>
<div className={s.imgandname}>
<img src={img} />
<div className={s.name}>{name}</div>
</div>
<div className={s.size}>{size}</div>
<div style={{ backgroundColor: `${color.color}` }} className={s.color}>
{color}
</div>
<div className={s.value}>1</div>
<div className={s.price}>{price}</div>
</div>
);
};
export default BasketItem;

passing state variables in react from child of one parent to another parent

I'm extremely new to react. i'm trying to build a single page e-commerce cart page.
live link : https://reliable-selkie-ebd5a9.netlify.app/
git repo : https://github.com/mtaz337/E-commer-SPA-React
where I fetched some fake data from an api. using react and css I rendered those products on the page. The functionality is whenever user press on the add to Cart button + - sign will pop up and products will be added to cart and according to product price and delivery charge a total will be shown. you can view the calculation when clicking on the cart button on header. the cart section user can delete the products from cart by clicking the cross icon. and there should be an clear cart button on clicking which will empty the cart and show no items.
however, I could not add the product number badge on the Cart button in the header and couldn't apply clear cart button functionality. and the remove items from cart functionality seems buggy. whenever i click remove icon from cart products removed should show 'add to cart' but the button disappears.
it's a really small project. please if you have time go through the git repo and will appreciate a lot.
the component breakdown is like this
</header>
</body>
</product>
</cart>
</cartProduct>
Here is the Product.js
import React, { useState } from 'react';
import './Product.css'
const Product = (props) => {
const {title, price, description, category,image, rating,count}= props.product;
const [addToCartClincked, setAddToCartClincked] = useState(false);
const [counts, setcounts] = useState(0);
//increase countser
const increase = () => {
setcounts(counts => counts + 1);
};
//decrease countser
const decrease = () => {
setcounts(counts => counts - 1);
};
return (
<div className="product">
<img src={image} alt="" />
<h3>{title.substring(0, 50)}</h3>
<h5 className='price'>৳{price}</h5>
<div className='cardFooter'>
{
!addToCartClincked || counts===-1|| props.product.count===0?
<button className='artBtn' id="addToCart" onClick={() =>{
setAddToCartClincked(true);
setcounts(0);
}
}><strong>Add to Cart</strong></button>
:
<>
<div className='counter' id="counter">
<button onClick ={()=>{
increase();
props.handleInviteProduct(props.product);
}}>+</button>
<p>{counts}</p>
<button onClick={()=>{
decrease();
props.handleRemoveProduct(props.product)
}}>-</button>
</div>
</>
}
</div>
</div>
);
};
export default Product;
how can I show the item count on the Cart button badge dynamically instead of '*'
here is the header.js
import React, { useState } from 'react';
import './Header.css';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome' ;
import { faShoppingCart} from '#fortawesome/free-solid-svg-icons';
const Header = () => {
const cart = <FontAwesomeIcon icon={faShoppingCart}/>
const cartShow = ()=>{
document.getElementById("cart").style.display="block";
document.getElementById("product-section").style.opacity= "0.5";
}
return (
<div className="header">
<img src='https://www.onlinelogomaker.com/blog/wp-content/uploads/2017/06/shopping-online.jpg'>
</img>
<span class="button_badge">*</span>
<button onClick={cartShow}>{cart}</button>
</div>
);
};
export default Header;
Here is Cart.js
import React, { useState } from 'react';
import './Cart.css'
import CartProduct from '../CartProduct/CartProduct';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome' ;
import { faClose} from '#fortawesome/free-solid-svg-icons';
const Cart = (props) => {
const {cart} = props;
const handleRemoveItem=(item)=>{
props.removeItem(item)
}
let total=0;
for(const product of cart){
total= total+product.cost;
}
const close = <FontAwesomeIcon icon={faClose}/>
const cartClose=()=>{
document.getElementById("cart").style.display="none"
document.getElementById("product-section").style.opacity= "1";
}
const openModal= () =>{
document.getElementById('myModal').style.display = 'block';
}
const closeModal= () =>{
document.getElementById('myModal').style.display = 'none';
}
return (
<div className="cart" id="cart">
<div id="cartHead">
<h1><b>CART</b></h1>
<button id="crossCart" onClick={cartClose}>{close}</button>
</div>
{
total.toFixed(2)!== "0.00" ?
<>
<h3><b>Your Items</b></h3>
<hr></hr>
{
cart.map(product => <CartProduct key={product.id} product={product} handleRemoveItem={handleRemoveItem}></CartProduct>)
}
<hr></hr>
<h4>Sub-total = ৳{total.toFixed(2)}</h4>
<h4>Delivery Charge = ৳100 </h4>
<hr></hr>
<h4><b>Grand-total = ৳{parseFloat(total.toFixed(2))+100}</b></h4>
<button id="checkOutBtn" onClick={openModal}>Proceed To Check Out</button>
<div id="myModal" className="modal">
<div className="modal-content">
<span className="close" onClick={closeModal}>×</span>
<img src='https://upload.wikimedia.org/wikipedia/commons/thumb/3/3b/Eo_circle_green_checkmark.svg/2048px-Eo_circle_green_checkmark.svg.png'></img>
<h2>Dear user, your order has been placed.</h2>
</div>
</div>
</>
:
<h2>No Items</h2>
}
</div>
);
};
export default Cart;
In Cart.js Line 53:
enter image description here
<h4><b>Grand-total = ৳{total + 100}</b></h4>
For this example, I think you could use a react context + useReducer,
with useReducer you can make a simple store and thanks to the context you can pass it to the children without prop drilling, in this way you can access the shop cart everywhere.
Here an example I found that could help you understand how to do that:
https://codesandbox.io/s/zwzv5x2jl?file=/src/context/ShopContext.js

how do I get my add to cart button to work in React (cart is not iterable) issue comes up in console?

When I click my add to cart button on the page it comes up with the error 'cart is not iterable' in console not sure what Im doing wrong as Im not sure I really understand the error. I have a separate shop and cart page. The shop has ad addtoCart button which works Thanks in Advance.
import React, { useState } from 'react';
import './Product.css';
import './Imagegallery.js';
export default function Product(product, cart, setCart){
const prod=product
const addToCart = (product) => {
let newCart = [...cart];
let itemInCart = newCart.find(
(item) => prod.product.name === item.name
);
if (itemInCart) {
itemInCart.quantity++;
} else {
itemInCart = {
...product,
quantity: 1,
};
newCart.push(itemInCart);
}
setCart(newCart);
};
return (
<>
<h1>Product Page</h1>
<div className="products">
<div className="product">
<h3>{prod.product.name}</h3>
<h4>£{prod.product.cost}</h4>
<p>{prod.product.description}</p>
<br />
<button onClick={() => addToCart(product)}>
Add to Cart
</button>
<section>
<div className="image-gallery">
<img className="gallery-highlight" img src={prod.product.image} alt=.
{prod.product.name} />
<div className="image-preview">
<img src={prod.product.image2} className="image-active" />
<img src={prod.product.image3} />
<br />
</div>
</div>
</section>
</div>
</div>
</>
);
}

Use same usestate hook for multiple icons

I have a two buttons in my react project (material ui icons) which are lowerarrow(both) and i made a use state and function to change upper arrow to lower arrow when it is clicked but i dont know how to use the same state for my another icon too, maybe using some id or something i dont know , right now i put {iconstate} in both the icons so both the icons are changing together i am trying to figure out how to change them individually without making new state and function here is my code :-
JSX:-
import React , {useState} from 'react';
import Weather_leftpanecss from './Weather_leftpane.module.css'
import KeyboardArrowDownIcon from '#mui/icons-material/KeyboardArrowDown';
import KeyboardArrowUpIcon from '#mui/icons-material/KeyboardArrowUp';
export default function Weather_leftpane() {
const [iconstate, seticonstate] = useState(<KeyboardArrowDownIcon/>)
const [valuestate, setvaluestate] = useState(true)
const togglearrow = ()=>{
if(valuestate==true){
seticonstate(<KeyboardArrowUpIcon/>)
setvaluestate(false)
}
else{
seticonstate(<KeyboardArrowDownIcon/>)
setvaluestate(true)
}
}
return <div>
<div className={Weather_leftpanecss.main}>
<div id={Weather_leftpanecss.item_1}>Weather</div>
<div id={Weather_leftpanecss.item_2}>
<input type="text" placeholder='Search for city..' />
</div>
<div id={Weather_leftpanecss.item_3}>
<div className={Weather_leftpanecss.item_3_content} id="item_3_1">
Cities
</div>
<div className={Weather_leftpanecss.item_3_content} id="item_3_2" onClick={togglearrow} >
{iconstate}
</div>
</div>
<div id={Weather_leftpanecss.item_4}>
<div className={Weather_leftpanecss.item_4_content} id="item_4_1">
Settings
</div>
<div className={Weather_leftpanecss.item_4_content} id="item_4_2" onClick={togglearrow}>
{iconstate}
</div>
</div>
</div>
</div>;
}
css:-
.App {
font-family: sans-serif;
text-align: center;
}
(not the real css ,just added it because i have included it everywhere in my code with module)
Here you need to save the selected Id in the state.
import React , {useState} from 'react';
import Weather_leftpanecss from './Weather_leftpane.module.css'
import KeyboardArrowDownIcon from '#mui/icons-material/KeyboardArrowDown';
import KeyboardArrowUpIcon from '#mui/icons-material/KeyboardArrowUp';
export default function Weather_leftpane() {
const [valuestate, setvaluestate] = useState(true)
const [id, setId] = useState(null);
const togglearrow = (val)=>{
if(valuestate==true){
setvaluestate(false)
}else{
setvaluestate(true)
}
setId(val);
}
return <div>
<div className={Weather_leftpanecss.main}>
<div id={Weather_leftpanecss.item_1}>Weather</div>
<div id={Weather_leftpanecss.item_2}>
<input type="text" placeholder='Search for city..' />
</div>
<div id={Weather_leftpanecss.item_3}>
<div className={Weather_leftpanecss.item_3_content} id="item_3_1">
Cities
</div>
<div className={Weather_leftpanecss.item_3_content} id="item_3_2" onClick={() => togglearrow('item_3_2')} >
{valuestate && id == 'item_3_2' ? KeyboardArrowUpIcon : KeyboardArrowDownIcon }
</div>
</div>
<div id={Weather_leftpanecss.item_4}>
<div className={Weather_leftpanecss.item_4_content} id="item_4_1">
Settings
</div>
<div className={Weather_leftpanecss.item_4_content} id="item_4_2" onClick={() => togglearrow('item_4_1')}>
{valuestate && id == 'item_4_1' ? KeyboardArrowUpIcon : KeyboardArrowDownIcon }
</div>
</div>
</div>
</div>;
}
Here you can use one state to achieve that, saving in one state the status of both buttons, which one is clicked or not and render the icon based on that status.
the toggleArrow function gets the itemId and uses it to set the updated value of the button. We use !prevState[itemId] since if it is false it will become true and vise versa.
I took the liberty of giving the state a more informative name than a generic name.
import React, { useState } from "react";
import Weather_leftpanecss from "./Weather_leftpane.module.css";
import KeyboardArrowDownIcon from "#mui/icons-material/KeyboardArrowDown";
import KeyboardArrowUpIcon from "#mui/icons-material/KeyboardArrowUp";
export default function Weather_leftpane() {
const [clickedButtons, setClickedButtons] = useState({
item_3_2: false,
item_4_2: false
});
const toggleArrow = (itemId) => {
setClickedButtons((prevState) => ({
...prevState,
[itemId]: !prevState[itemId]
}));
};
return (
<div>
<div className={Weather_leftpanecss.main}>
<div id={Weather_leftpanecss.item_1}>Weather</div>
<div id={Weather_leftpanecss.item_2}>
<input type="text" placeholder="Search for city.." />
</div>
<div id={Weather_leftpanecss.item_3}>
<div className={Weather_leftpanecss.item_3_content} id="item_3_1">
Cities
</div>
<div
className={Weather_leftpanecss.item_3_content}
id="item_3_2"
onClick={() => toggleArrow("item_3_2")}
>
{clickedButtons["item_3_2"] ? (
<KeyboardArrowUpIcon />
) : (
<KeyboardArrowDownIcon />
)}
</div>
</div>
<div id={Weather_leftpanecss.item_4}>
<div className={Weather_leftpanecss.item_4_content} id="item_4_1">
Settings
</div>
<div
className={Weather_leftpanecss.item_4_content}
id="item_4_2"
onClick={() => toggleArrow("item_4_2")}
>
{clickedButtons["item_4_2"] ? (
<KeyboardArrowUpIcon />
) : (
<KeyboardArrowDownIcon />
)}
</div>
</div>
</div>
</div>
);
}

How to open dynamic modal with react js

I am trying to convert the HTML/Javascript modal to React js.
In Reactjs, I just want to open the modal whenever the user clicks the View Project button.
I have created a parent component (Portfolio Screen) and a child component (Portfolio Modal). The data I have given to the child component is working fine but the modal opens the first time only and then does not open. Another problem is that the data does not load even when the modal is opened the first time.
Codesandbox link is here.
https://codesandbox.io/s/reverent-leftpad-lh7dl?file=/src/App.js&resolutionWidth=683&resolutionHeight=675
I have also shared the React code below.
For HTML/JavaScript code, here is the question I have asked before.
How to populate data in a modal Popup using react js. Maybe with hooks
Parent Component
import React, { useState } from 'react';
import '../assets/css/portfolio.scss';
import PortfolioModal from '../components/PortfolioModal';
import portfolioItems from '../data/portfolio';
const PortfolioScreen = () => {
const [portfolio, setportfolio] = useState({ data: null, show: false });
const Item = (portfolioItem) => {
setportfolio({
data: portfolioItem,
show: true,
});
};
return (
<>
<section className='portfolio-section sec-padding'>
<div className='container'>
<div className='row'>
<div className='section-title'>
<h2>Recent Work</h2>
</div>
</div>
<div className='row'>
{portfolioItems.map((portfolioItem) => (
<div className='portfolio-item' key={portfolioItem._id}>
<div className='portfolio-item-thumbnail'>
<img src={portfolioItem.image} alt='portfolio item thumb' />
<h3 className='portfolio-item-title'>
{portfolioItem.title}
</h3>
<button
onClick={() => Item(portfolioItem)}
type='button'
className='btn view-project-btn'>
View Project
</button>
</div>
</div>
))}
<PortfolioModal portfolioData={portfolio} show={portfolio.show} />
</div>
</div>
</section>
</>
);
};
export default PortfolioScreen;
Child Component
import React, { useState, useEffect } from 'react';
import { NavLink } from 'react-router-dom';
const PortfolioModal = ({ portfolioData, show }) => {
const portfolioItem = portfolioData;
const [openModal, setopenModal] = useState({ showState: false });
useEffect(() => {
setopenModal({
showState: show,
});
}, [show]);
return (
<>
<div
className={`portfolio-popup ${
openModal.showState === true ? 'open' : ''
}`}>
<div className='pp-inner'>
<div className='pp-content'>
<div className='pp-header'>
<button
className='btn pp-close'
onClick={() =>
setopenModal({
showState: false,
})
}>
<i className='fas fa-times pp-close'></i>
</button>
<div className='pp-thumbnail'>
<img src={portfolioItem.image} alt={`${portfolioItem.title}`} />
</div>
<h3 className='portfolio-item-title'>{portfolioItem.title}</h3>
</div>
<div className='pp-body'>
<div className='portfolio-item-details'>
<div className='description'>
<p>{portfolioItem.description}</p>
</div>
<div className='general-info'>
<ul>
<li>
Created - <span>{portfolioItem.creatDate}</span>
</li>
<li>
Technology Used -
<span>{portfolioItem.technologyUsed}</span>
</li>
<li>
Role - <span>{portfolioItem.Role}</span>
</li>
<li>
View Live -
<span>
<NavLink to='#' target='_blank'>
{portfolioItem.domain}
</NavLink>
</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</>
);
};
export default PortfolioModal;
You don't have to use one useState hook to hold all your states. You can and I think you should break them up. In the PortfolioScreen component
const [data, setData] = useState(null);
const [show, setShow] = useState(false);
I changed the function Item that is used to set the active portfolio item to toggleItem and changed it's implementation
const toggleItem = (portfolioItem) => {
setData(portfolioItem);
setVisible(portfolioItem !== null);
};
You should use conditional rendering on the PortfolioModal, so you won't need to pass a show prop to it, and you'll pass a closeModal prop to close the PortfolioModal when clicked
{visible === true && data !== null && (
<PortfolioModal
data={data}
closeModal={() => toggleItem()} // Pass nothing here so the default value will be null and the modal reset
/>
)}
Then in the PortfolioModal component, you expect two props, data and a closeModal function
const PortfolioModal = ({ data, closeModal }) => {
And the close button can be like
<button className="btn pp-close" onClick={closeModal}>
...

Resources