Reactjs Update the state without reload the page - reactjs

I built a shopping cart in my app. This is the flow:
- The customer sees a list of items and clicks on one he wants;
- The next page is where he chooses the quantity of products and then I save in localStorage;
- By clicking Confirm, he goes to the shopping cart with the same products he has chosen. On this page (shopping cart) he can change the quantity and in this moment the total and quantity must change (see the image).
I was able to do this reloading the page, but when in production the page is not working properly. I need to do this without reload the page.
How to do this without reload the page?
I got 3 components: Header (with the icon cart), ChooseQuantity and Cart.
See below the code:
//My Choose Quantity Component
import React from 'react';
import '../../components/ChooseQuantity/ChooseQuantity.css';
class ChooseQuantity extends React.Component {
constructor(props) {
super(props);
const { lotQuantity, totalQuantity, maxTotalItems, maxPurchase, lot, totalTickets, events, onChange } = this.props;
this.state = {
counter: 0,
lotQuantity: lotQuantity,
totalQuantity: totalQuantity,
maxTotalItems: maxTotalItems,
maxPurchase: maxPurchase,
totalTickets: totalTickets,
onChange: onChange,
events: events,
lot: lot,
tickets: events.tickets,
cart: []
}
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
}
componentDidMount() {
// console.log(this.state.lot);
// localStorage.setItem('teste', JSON.stringify(this.state.lotQuantity));
}
// static getDerivedStateFromProps(props, state) {
// console.log(props.lot);
// // console.log(state);
// if (props.selected !== state.selected) {
// return {
// selected: props.selected,
// };
// }
// }
// componentDidUpdate(prevProps, prevState) {
// console.log(this.props.lot);
// localStorage.setItem('teste', JSON.stringify(this.props.lot.quantity));
// if (this.props.lot !== prevProps.lot) {
// // this.selectNew();
// }
// }
async increment() {
await this.setState({
lotQuantity: this.state.lotQuantity + 1,
totalQuantity: + 1,
});
let lotUniqueNumber = this.state.lot.lotUniqueNumber;
let lotQuantity = this.state.lotQuantity;
var ar_lot = [];
this.state.tickets.lot.forEach(function (item) {
if (lotUniqueNumber === item.lotUniqueNumber) {
item.quantity = lotQuantity;
item.total = item.totalLotPrice * item.quantity
}
ar_lot.push(item);
})
// console.log(ar_lot);
//CALCULATING A QUANTITY
var ob_qtd = ar_lot.reduce(function (prevVal, elem) {
const ob_qtd = prevVal + elem.quantity;
return ob_qtd;
}, 0);
await this.setState({ totalTickets: ob_qtd })
//CALCULATING A QUANTITY
//CALCULATING THE TOTAL
var ob_total = ar_lot.reduce(function (prevVal, elem) {
const ob_total = prevVal + elem.total;
return ob_total;
}, 0);
// CALCULATING THE TOTAL
//RIDING THE SHOPPING CART
let total = {
price: ob_total,
totalQuantity: ob_qtd,
};
let tickets = {
name: this.state.tickets.name,
prevenda: this.state.tickets.prevenda,
unique_number: this.state.tickets.unique_number,
lot: ar_lot
}
let events = {
banner_app: this.state.events.banner_app,
installments: this.state.events.installments,
max_purchase: this.state.events.max_purchase,
name: this.state.events.name,
tickets: tickets
}
var cart = { events: events, total: total };
this.setState({
cart: cart
})
// console.log(cart);
localStorage.setItem('cart', JSON.stringify(cart));//RECORDING CART IN LOCALSTORAGE
localStorage.setItem('qtd', JSON.stringify(ob_qtd));
window.location.reload();//UPDATE PAGE FOR CHANGES TO BE UPDATED
}
async decrement() {
await this.setState({
lotQuantity: this.state.lotQuantity - 1,
totalQuantity: - 1,
totalTickets: this.state.totalTickets - 1,
});
let lotUniqueNumber = this.state.lot.lotUniqueNumber;
let lotQuantity = this.state.lotQuantity;
var ar_lot = [];
this.state.tickets.lot.forEach(function (item) {
if (lotUniqueNumber === item.lotUniqueNumber) {
item.quantity = lotQuantity;
item.total = item.totalLotPrice * item.quantity
}
ar_lot.push(item);
})
//CALCULANDO A QUANTIDADE
var ob_qtd = ar_lot.reduce(function (prevVal, elem) {
const ob_qtd = prevVal + elem.quantity;
return ob_qtd;
}, 0);
//CALCULANDO A QUANTIDADE
//CALCULANDO O TOTAL
var ob_total = ar_lot.reduce(function (prevVal, elem) {
const ob_total = prevVal + elem.total;
return ob_total;
}, 0);
//CALCULANDO O TOTAL
let total = {
price: ob_total,
totalQuantity: ob_qtd,
};
let tickets = {
name: this.state.tickets.name,
prevenda: this.state.tickets.prevenda,
unique_number: this.state.tickets.unique_number,
lot: ar_lot
}
let events = {
banner_app: this.state.events.banner_app,
installments: this.state.events.installments,
max_purchase: this.state.events.max_purchase,
name: this.state.events.name,
tickets: tickets
}
var cart = { events: events, total: total };
localStorage.setItem('cart', JSON.stringify(cart));
localStorage.setItem('qtd', JSON.stringify(ob_qtd));
window.location.reload();
}
render() {
return (
<div className="choose-quantity">
{
this.state.lotQuantity <= 0 ?
<div className="space-button"></div> :
<button className='minus' onClick={this.decrement}><i className="fas fa-minus"></i></button>
}
<div id='counter' className="qtd" value={this.state.lotQuantity} onChange={this.onChange}>{this.state.lotQuantity}</div>
{
this.state.totalTickets >= this.state.maxPurchase ?
<div className="space-button"></div> :
<button className="plus" onClick={() => this.increment(this.state.lotQuantity)}><i className="fas fa-plus"></i></button>
}
</div>
)
}
}
export default ChooseQuantity;
//My Shopping Cart Component
import React, { Component } from 'react';
import Swal from "sweetalert2";
import { Link } from 'react-router-dom';
import './Cart.css';
import '../../components/Css/App.css';
import Lot from './Lot';
import ChooseQuantity from './ChooseQuantity';
import Header from '../../components/Header/Header';
import Tabbar from '../../components/Tabbar/Tabbar';
const separator = '/';
class Cart extends Component {
constructor(props) {
super(props);
this.state = {}
this.choosePayment = this.choosePayment.bind(this);
}
async componentDidMount() {
const company_info = JSON.parse(localStorage.getItem('company_info'));
await this.setState({
company_image: company_info.imagem,
company_hash: company_info.numeroUnico,
})
const cart = JSON.parse(localStorage.getItem('cart'));
const total = cart.total;
if(cart){
const {
events,
events: { tickets },
total
} = cart;
await this.setState({
cart,
events,
tickets: tickets,
banner_app: events.banner_app,
eventName: cart.events.name,
priceTotal: total.price,
quantity: total.totalQuantity,
lots: tickets.lot,
maxTotalItems: cart.events.max_purchase,
selectedLots: tickets.lot,
total: total.totalQuantity
});
}
const teste = JSON.parse(localStorage.getItem('teste'))
this.setState({teste: teste})
}
choosePayment() {
Swal.fire({
title: 'Método de Pagamento',
text: 'Qual o médtodo de pagamento que você deseja usar?',
confirmButtonText: 'Cartão de Crédito',
confirmButtonColor: '#007bff',
showCancelButton: true,
cancelButtonText: 'Boleto Bancário',
cancelButtonColor: '#007bff',
}).then((result) => {
if (result.value) {
this.props.history.push('/checkout');
} else{
this.props.history.push('/checkout-bank-slip');
}
})
}
render() {
return (
<div>
<Header Title="Carrinho" ToPage="/" />
{
this.state.total <= 0 ?
<Tabbar />
:
null
}
<div className="cart">
<div className="container-fluid">
{
this.state.total > 0 ?
<div>
<div className="box-price">
<div className="row box-default ">
<div className="col col-price">
<h6>{this.state.quantity} INGRESSO{this.state.quantity > 1 ? 'S' : ''}</h6>
<h5>R$ {parseFloat(this.state.priceTotal).toFixed(2).replace('.', ',')}</h5>
</div>
</div>
</div>
<div className="row">
<div className="col-12 col-image no-padding">
<img src={this.state.banner_app} alt="" />
</div>
</div>
<div className="row">
<div className="col">
<h1 className="event-title text-center">{this.state.eventName}</h1>
</div>
</div>
<div className="padding-15">
{
this.state.lots.map((lot, l) =>
<div key={l}>
{
lot.quantity > 0 ?
<div>
<div className="row">
<div className="col">
<h5 className="ticket-name">{lot.ticketName}</h5>
</div>
</div>
<div className="row">
<div className="col-8">
<h5 className="lot-name">
{ lot.lotName } - ({lot.lotNumber}º Lote)
</h5>
<h6 className="lot-price">
R$ {lot.lotPrice.replace('.', ',')} ({lot.lotPrice.replace('.', ',')} + {lot.lotPriceTax.replace('.', ',')})
</h6>
</div>
<div className="col-4">
<h3 className='lot-big-price'>
{lot.lotPrice.replace('.', ',')}
</h3>
</div>
</div>
<div className="row">
<div className="col align-items">
<ChooseQuantity
lotQuantity={lot.quantity}
maxPurchase={this.state.events.max_purchase}
totalTickets={this.state.total}
lot={lot}
events={this.state.events}
maxTotalItems={this.state.maxTotalItems}
onCLick={this.onClick}
/>
</div>
</div>
</div>
:
null
}
</div>
)
}
<div className="row cart-footer" style={{ marginRight: '-15px', marginLeft: '-15px', backgroundColor: '#f4f7fa' }}>
<button className="col col-purchase" style={{ justifyContent: 'center', alignItems: 'center' }} onClick={this.choosePayment}>
Confirmar Comprar
</button>
</div>
</div>
</div>
:
<div className='padding-15'>
<div className="mt-5 no-margin box-default row">
<div className="col">
<h3 className="text-center">
Não há nenhum item em seu carrinho.
</h3>
<p className="text-center">
Toque no botão <strong>Buscar Eventos</strong> para iniciar uma nova pesquisa.
</p>
<Link className="btn btn-primary btn-block" to="/">
Buscar Eventos
</Link>
</div>
</div>
<div className="row no-margin box-default mt-3">
<div className="col">
<img src={`//www.yeapps.com.br/admin/files/empresa/${this.state.company_hash}/${this.state.company_image}`} alt={`${this.state.company_name}`} />
</div>
</div>
</div>
}
</div>
</div>
</div>
);
}
}
export default Cart;
//My Header Component
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
// import { withRouter } from 'react-router';
import './Header.css';
import BackButton from '../BackButton/BackButton';
class Header extends Component {
constructor(props){
super(props);
this.state = {
qtd: 0
}
}
componentDidMount() {
const qtd = JSON.parse(localStorage.getItem('qtd'));
this.setState({qtd: qtd});
}
render() {
const { Title } = this.props;
return (
<div>
<nav className="navbar">
{ this.props.Title === 'Home' ? null : <BackButton />}
<Link to="/cart" className="icon-cart">
<i className="fas fa-shopping-cart"></i>
<span className="badge badge-danger">
{this.state.qtd}
</span>
</Link>
<div className="navbar-brand">
{Title}
</div>
</nav>
</div>
);
}
}
export default Header;

You need to update your presentational components(Card,Header) state in componentDidUpdate the same way as you did it in componentDidMount. componentDidMount works only one time with first render and there you set your state variables which you use in render. Do the same setState in componentDidUpdate
P.S. but you need to be sure that with counter increasing your presentational components get new props which will trigger componentDidUpdate.In your case you use localStorage but it will be better to pass data through common parent container(the one which holds those 3 components) as props to Card and Header.

Related

how to add data in localstorage in react

I am arslan Chaudhry. currently, I am working on an eCommerce site where I will store data in local storage because I don't have too much backend knowledge. how I can add functionality like delete and update on another folder.
my code is given below.
Books.js
import React from "react";
import "./components/book.css";
import Carousel from "react-multi-carousel";
import "react-multi-carousel/lib/styles.css";
import { FaShoppingCart } from "react-icons/fa";
import { useEffect } from "react";
import { useState, useRef } from "react";
import { useNavigate } from "react-router-dom";
import { createContext } from "react";
const Books = () => {
let arr=()=>{
let dat=localStorage.getItem("products")
if (dat) {
return JSON.parse(dat)
}
else{
return []
}
}
const [booksData, setbooksData] = useState([]);
const [productData, setproductData] = useState(arr());
let nav = useNavigate();
// slider
const responsive = {
superLargeDesktop: {
breakpoint: { max: 4000, min: 3000 },
items: 5,
},
desktop: {
breakpoint: { max: 3000, min: 1024 },
items: 3,
},
tablet: {
breakpoint: { max: 1024, min: 464 },
items: 2,
},
mobile: {
breakpoint: { max: 464, min: 0 },
items: 1,
},
};
let croser = useRef("");
let loding = useRef("");
const getJason = async () => {
try {
let fea = await fetch(
"https://script.google.com/macros/s/AKfycbxFCG7S-kjncQZwvcMnqq4wXoBAX8ecH1zkY2bLP7EE-YHlnKbiJ3RUuHtWLe6sIK30Kw/exec"
);
let acData = await fea.json();
let itemsData = acData.shop.filter((element) => {
if (element.name) {
return element;
}
});
setbooksData(itemsData);
if (itemsData) {
croser.current.style.filter = "blur(0px)";
loding.current.style.display = "none";
}
} catch (error) {
croser.current.style.filter = "blur(0px)";
loding.current.style.display = "none";
}
};
// get product data from api
useEffect(() => {
getJason();
}, []);
// go to cart button
const goto = () => {
nav("/Cart");
};
// local data
let addNow=(e)=>{
let data=productData.find((element)=>{return element.id === e.id });
let cart;
if (data) {
cart=productData.map((element)=>{
return element.id === e.id ? {...element, quantity:element.quantity+1}:element
})
}
else{
cart=[...productData,{...e, quantity:1}]
}
setproductData(cart);
};
useEffect(() => {
localStorage.setItem("products",JSON.stringify(productData))
}, [productData])
console.log(productData);
return (
<>
<div className="row " style={{ marginTop: "10px" }}>
<div className="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div className="section-headline text-center">
<h2>Books Shop</h2>
</div>
</div>
</div>
<div className="lodingBooks" ref={loding}>
<div class="spinner-border" role="status"></div>
<h4>Please wait....</h4>
</div>
<div ref={croser}>
<div className=" shadow go_to_cart" onClick={goto}>
<i class="bi bi-cart-check text-white"></i>
</div>
<Carousel responsive={responsive} className="container">
{booksData.map((element) => {
return (
<>
<div class="container page-wrapper">
<div class="page-inner">
<div class="row">
<div class="el-wrapper">
<div class="box-up">
<img class="img" src={element.images} alt="" />
<div class="img-info">
<div class="info-inner">
<span class="p-name text-info">
{element.name}
</span>
<span class="p-company ">Author:CHAUDHRY</span>
</div>
<div class="a-size ">
About:This is a complete book on javascript
<span class="size"></span>
</div>
</div>
</div>
<input
type="text"
value={1}
style={{ display: "none" }}
/>
<div class="box-down">
<div class="h-bg">
<div class="h-bg-inner"></div>
</div>
<a class="cart">
<span class="price">{element.price + "$"}</span>
<span
class="add-to-cart btn btn-sm"
style={{ backgroundColor: "#3EC1D5" }}
onClick={()=>{addNow(element)}}
>
<span class="txt">
ADD TO CART <FaShoppingCart />
</span>
</span>
</a>
</div>
</div>
</div>
</div>
</div>
</>
);
})}
</Carousel>
</div>
</>
);
};
export default Books;
and here is my cart file. where i want to perform the action like update and delete.
Cart.js
import React from "react";
import "./components/cart.css";
import { useEffect } from "react";
const Cart = () => {
let data = localStorage.getItem("products");
let javaData = JSON.parse(data);
let removeData = (e) => {
};
useEffect(() => {
localStorage.clear()
}, [])
return (
<>
<div class="container mt-5 mb-5">
<div class="d-flex justify-content-center row">
<div class="col-md-8">
<div class="p-2 shoingTitle">
<h4>Shop Now</h4>
<span class="text-danger">Remove all</span>
</div>
{javaData ? (
javaData.map((item) => {
return (
<>
<div class="d-flex flex-row justify-content-between align-items-center p-2 bg-white mt-4 px-3 rounded">
<div class="mr-1 imageandpara">
<img class="rounded" src={item.images} width="70" />
<span class="font-weight-bold">{item.name}</span>
</div>
<div class="d-flex flex-column align-items-center product-details">
<div class="d-flex flex-row product-desc"></div>
</div>
<div class="d-flex flex-row align-items-center qty">
<i class="minusSign shadow">
<i class="bi bi-dash"></i>
</i>
<span class="text-grey quantityNumber">
{item.quantity}
</span>
<i class="minusSign shadow">
<i class="bi bi-plus"></i>
</i>
</div>
<div>
<span class="text-grey productAmount">{`${
item.quantity * item.price
}$`}</span>
</div>
<div
class="d-flex align-items-center text-dark "
style={{
cursor: "pointer",
fontWeight: "900",
fontSize: "15px",
}}
onClick={() => {
removeData(item);
}}
>
<i class="bi bi-x text-danger"></i>
</div>
</div>
</>
);
})
) : (
<h3 style={{ textAlign: "center" }}>Cart is empety</h3>
)}
<div class="d-flex flex-row align-items-center mt-3 p-2 bg-white rounded">
<input
type="text"
class="form-control gift-card "
placeholder="discount code/gift card"
/>
<button
class="btn btn-sm ml-3 shadow"
type="button"
style={{
outline: "#3EC1D5",
backgroundColor: "#3EC1D5",
color: "white",
}}
>
Apply
</button>
</div>
<div class="totalItems">
Total Items: <strong>12</strong>
</div>
<span class="TotalPrice">
Total price: <strong>12$</strong>
</span>
<div class="d-flex flex-row align-items-center mt-3 p-2 bg-white rounded">
<button
class="btn btn-block btn-sm ml-2 pay-button shadow"
type="button"
style={{ backgroundColor: "#3EC1D5" }}
>
Proceed to Pay
</button>
</div>
</div>
</div>
</div>
</>
);
};
export default Cart;
Try this for add:
let removeData = (e) => {
localStorage.setItem("name of the item") // e.target.name
};
There's alot going on in your site :)
I think it will be responsible to create a context, that will serve the cart to all other components.
Things to note here, (Found some little improvements)
Run the function in the useState hook, don't just leave it there like you did
When using Array.filter you need to return a boolean iorder for it too filter your array properly.
This is the code I brought up hope it helps you out.
CartContext.js file.
import React, { createContext, useContext, useEffect, useState } from "react";
export const CartContext = createContext();
function Cart({ children }) {
const arr = useCallback(() => {
let dat = localStorage.getItem("products");
if (dat) {
return JSON.parse(dat);
} else {
return [];
}
}, []);
const [productData, setproductData] = useState(() => arr());
const getJason = async () => {
try {
let fea = await fetch(
"https://script.google.com/macros/s/AKfycbxFCG7S-kjncQZwvcMnqq4wXoBAX8ecH1zkY2bLP7EE-YHlnKbiJ3RUuHtWLe6sIK30Kw/exec"
);
let acData = await fea.json();
// filter callback function should return a boolean. That is either true or false in order to make it work.
// SO i think this function isn't going to function properly
let itemsData = acData.shop.filter((element) => {
if (element.name) {
return element;
}
});
setbooksData(itemsData);
if (itemsData) {
croser.current.style.filter = "blur(0px)";
loding.current.style.display = "none";
}
} catch (error) {
croser.current.style.filter = "blur(0px)";
loding.current.style.display = "none";
}
};
// get product data from api
useEffect(() => {
getJason();
}, []);
const addProduct = (e) => {
// check if product id available on cart
const findProduct = productData.find((element) => {
return element.id === e.id;
});
// add first quantity if not available
if (!findProduct)
return setproductData([...productData, { ...e, quantity: 1 }]);
// increase quantity by 1
const newCart = productData.map((element) => {
return {
...element,
quantity: element.id === e.id ? element.quantity + 1 : element.quantity,
};
});
setproductData(newCart);
};
const removeProduct = (e) => {
// check if product id available on cart
const findProductQuantity = productData.find((element) => {
return element.id === e.id && element.quantity >= 1;
});
// add first quantity if not available
if (!findProduct)
// Your ui should prevent this
return;
// decrease quantity by 1
const reducedQuantityCart = productData.map((element) => {
return {
...element,
quantity: element.id === e.id ? element.quantity - 1 : element.quantity,
};
});
// filter out all products with quantity less than 1 (quantity : 0)
const newCart = productData.filter((element) => {
return element.quantity >= 1;
});
setproductData(newCart);
};
const deleteProduct = (e) => {
// check if product id available on cart
const findProduct = productData.find((element) => {
return element.id === e.id;
});
// add first quantity if not available
if (!findProduct)
// Your ui should prevent this
return;
const productIndex = productData.findIndex((element) => {
return element.id === e.id;
});
// splice (delete) product from the productData array
const newCart = [productData].splice(productIndex, 1);
setproductData(newCart);
};
const value = {
productData,
addProduct,
removeProduct,
deleteProduct,
};
return <CartContext.Provider value={value}>{children}</CartContext.Provider>;
}
In here you create a context, create all your function you will use to update your cart, and pass them to your Context provider
// create a hook can you use anywhere in the app
export const useCart = () => {
const context = useContext(CartContext);
if (!context) {
throw new Error("Must use useCart in CartContext Child");
}
const { productData, addProduct, removeProduct, deleteProduct } = context;
return { productData, addProduct, removeProduct, deleteProduct };
};
For this you create a custom hook that you can use inside any component provided it is a child of the Cart context. So make sure you wrap it all around your app
// Use Case
const SomeComponent = () => {
const { productData, addProduct, removeProduct } = useCart();
return (
<div>
<p> Number of Products: {productData.length}</p>
<div>
{productData?.map((product) => (
<div>
<p>{product?.name}</p>
<button onClick={() => addProduct(product)}>add</button>
<button onClick={() => removeProduct(product)}>
subtract/reduce
</button>
<button onClick={() => deleteProduct(product)}>delete</button>
</div>
))}
</div>
</div>
);
};
Use case Scenario od how this code will work. Hope you find this helful

How can I do in React so that once a product is added to the cart if that product has already been added before it does not repeat a new object?

[ I need to avoid the duplication of an object that has already been added to the cart, so that once I add it again, I only add the quantity property of the previous object, which would be the same ]
[CartContext.js]
import React, { createContext, useState } from "react";
export const CarritoContext = createContext();
export default function CartContext({ children }) {
const [addToCarrito, setAddToCarrito] = useState([]);
[This is the function that I cannot modify so that it complies with a certain rule of not adding duplicates of an object that has already been added to the cart, only increasing its quantity of the already added object]
function addItem(item, quantity) {
setAddToCarrito(
addToCarrito.filter((elemento, pos) => {
if (elemento.item.id === item.id) {
addToCarrito[pos].quantity += quantity;
return false;
}
return true;
})
);
if (quantity === 0) {
setAddToCarrito([...addToCarrito]);
} else {
setAddToCarrito([...addToCarrito, { item, quantity }]);
}
}
function clear() {
setAddToCarrito([]);
}
function removeItem(itemId) {
const newItems = addToCarrito.filter((item) => item.item.id !== itemId);
setAddToCarrito(newItems);
}
console.log(addToCarrito);
return (
<>
<CarritoContext.Provider
value={{ addToCarrito, setAddToCarrito, clear, addItem, removeItem }}
>
{children}
</CarritoContext.Provider>
</>
);
}
[ItemAside.js]
import React, { useState, useContext } from "react";
import { Link } from "react-router-dom";
import ItemCount from "../ItemCount/ItemCount";
import { CarritoContext } from "../../context/CartContext";
const ItemAside = ({ jackets }) => {
const [quantityCarro, setQuantityCarro] = useState(0);
const [branded] = useState(jackets.brand);
let { addToCarrito } = useContext(CarritoContext);
let { setAddToCarrito } = useContext(CarritoContext);
let { clear } = useContext(CarritoContext);
let { addItem } = useContext(CarritoContext);
let { removeItem } = useContext(CarritoContext);
const onAdd = (cantidadCarro) => {
setQuantityCarro(cantidadCarro);
setAddToCarrito([
...addToCarrito,
{ item: jackets, quantity: cantidadCarro }
]);
addItem(jackets, cantidadCarro);
};
return (
<div className="container-vertical">
<aside style={{ width: "100%" }}>
<div className="container-cuadrado">
<div>
<h3>${jackets.price}</h3>
</div>
<div>
<p>and FREE Returns</p>
</div>
<div>
<p>
Delivery for <strong>$39.99</strong>
</p>
<p>
between <strong>17 - 30 April</strong>
</p>
</div>
<div>
<small>
<span className="ubicacion"></span> Deliver to Argentina
</small>
</div>
<div>
{quantityCarro ? (
<>
<Link to="/cart">
<button className="close zbutton">Buy now</button>
</Link>
<p onClick={clear}>Limpiar carro </p>
<br />
<br />
<p onClick={() => removeItem(jackets.id)}>
{`Quitar ${jackets.name} de el carro`}
</p>
</>
) : (
<ItemCount
stock={jackets.stock}
branded={branded}
initial={0}
onAdd={onAdd}
/>
)}
</div>
<div>
<div className="celwidget">
<div className="a-section a-spacing-small a-text-left celwidget">
<span className="a-declarative">
<span className="aok-align-center">
<img
alt=""
src="https://images-na.ssl-images-amazon.com/images/G/30/x-locale/checkout/truespc/secured-ssl._CB485936936_.png"
height="15px"
/>
</span>
<span className="a-letter-space"></span>
<span
className="dataspan"
style={{
cursor: "pointer",
color: "#0099C0",
}}
data-hover="We work hard to protect your security and privacy. Our payment security system encrypts your information during transmission. We don’t share your credit card details with third-party sellers, and we don’t sell your information to others."
>
Secure transaction
</span>
</span>
</div>
</div>
</div>
<div className="info-shipping">
<div>
<p>Shipping from</p>
<p>Sold by</p>
</div>
<div>
<p>Carvel</p>
<p>Carvel</p>
</div>
</div>
<div className="gift-container">
<label className="control control--checkbox">
<input type="checkbox" className="checkgift" />
<small className="small-gift">
Add a gift ticket to facilitate returns
</small>
</label>
</div>
</div>
</aside>
</div>
);
};
export default ItemAside;
I think filter is not the correct function to use. Filter needs to filter object. You are trying to mutate them as well inside. The filter function is immutable which means that it generates a new array instead of an old one.
addToCarrito.filter((elemento, pos) => {
if (elemento.item.id === item.id) {
addToCarrito[pos].quantity += quantity; // <- mutating prev value(bad)
return false;
}
return true;
})
For this task is better to use an object, rather than an array. That will simplify a lot your code.
const [products, setProducts] = useState({})
const addProduct = (item, quantity) => {
// if product already in the object - take it, otherwise add new with 0 quatity
const newProduct = { ...(product[item.id] ?? {...item, quantity: 0}) }
newProduct.quantity += 1
setProducts(products => ({
…products,
[item.id]: newProduct
})
})
}
// here the array of your products, f.e. for iteration
const productList = React.useMemo(() => Object.values(products), [products])

How to get the promise value in reactjs?

I am trying to render multiple images, stored on firebase, on a single page.
This is the function to get the image url:
async getTokenImage(fileName) {
const ref = firebase.storage().ref('token/' + fileName);
const url = await ref.getDownloadURL();
console.log('in here')
return url
}
This is the function call:
<div
class="card-img-top"
style={{
backgroundColor: '#'+token.color,
backgroundImage: 'url('+this.getTokenImage(token.fileName).then((value) => {return value})+')'
}}></div>
console logging the "value" gives the needed image url
but returning the "value" gives nothing.
for the sake of testing, I have done this:
let image = this.getTokenImage(token.fileName).then((result) => {return result})
console.log(image)
which results in:
So how do I get the image url within the initial function call?
Part of grandfather component:
<MintedTokens
account={this.state.account}
contract={this.state.contract}
tokens={this.state.tokens} />
Father component:
import React from 'react';
import "./minted-tokens.scoped.css";
import MultipleMintedTokens from './multiple-minted-tokens.js';
import SingleMintedTokens from './single-minted-token.js';
class MintedTokens extends React.Component {
render() {
const color = window.location.pathname.split('/')[2]
let display
if(color == undefined) {
display = <MultipleMintedTokens
account={this.props.account}
tokens={this.props.tokens}></MultipleMintedTokens>
} else {
display = <SingleMintedTokens
account={this.props.account}
color={color}
tokens={this.props.tokens}></SingleMintedTokens>
}
return (
<div class="Minted-Token">
{display}
</div>
);
}
}
export default MintedTokens;
Child component (where the multiple images need to be rendered):
import React from 'react';
import ether from '../../assets/ether.svg'
import firebase from "../../firebase.js"
import { Link } from 'react-router-dom';
class MultipleMintedTokens extends React.Component {
async getTokenImage(fileName) {
const ref = firebase.storage().ref('token/' + fileName);
const url = await ref.getDownloadURL();
console.log('in here')
return url
}
render() {
return (
<div class="welcome minted-tokens">
<h1 class="">Minted Tokens</h1>
<div class="row">
{this.props.tokens.map((token, key) => {
let name = '',
description = ''
if(token.to == this.props.account) {
token.name.length >= 17 ? name = token.name.slice(0,17) + '...' : name = token.name
token.description.length >= 28 ? description = token.description.slice(0,29) + '...' : description = token.description
let image = this.getTokenImage(token.fileName).then((result) => {return result})
console.log(image)
return (
<Link to={token.tokenURI} key={key}>
<div class='token-id'>{token.id}</div>
<div class="card card-width">
<div
class="card-img-top"
style={{
backgroundColor: '#'+token.color,
backgroundImage: 'url('+this.getTokenImage(token.fileName).then((value) => {return value})+')'
}}></div>
<h5 class="card-header" style={{
backgroundColor: '#'+token.color,
color: '#'+token.fontColor}}>{name}</h5>
<div class="card-body">
<p class="card-text">{description}</p>
<div class="foot-of-card">
<span class="row price-row">
<img src={ether} alt="ether" class="icon" />
<p class="card-text">{token.price}</p>
</span>
<p class="card-text datetime">{token.dateTime}</p>
</div>
</div>
</div>
</Link>
)
}
})}
</div>
</div>
);
}
}
export default MultipleMintedTokens;
I could not test it well but you can try the following:
Add:
constructor(props) {
super(props);
this.state = {
urlImgs: [],
};
}
...
componentDidMount() {
const { tokens } = this.props;
const promiseArray = tokens.map((token) => getTokenImage(token.fileName));
Promise.all(promiseArray)
.then(valueArray => {
this.setState(prevState => ({
...prevState,
urlImgs: valueArray
}))
})
.catch(err => console.log(err));
}
...
backgroundImage: `url(${urlImgs[key] ?? null})`
All Code:
class MultipleMintedTokens extends React.Component {
constructor(props) {
super(props);
this.state = {
urlImgs: [],
};
}
async getTokenImage(fileName) {
const ref = firebase.storage().ref('token/' + fileName);
const url = await ref.getDownloadURL();
console.log('in here')
return url
}
componentDidMount() {
const { tokens } = this.props;
const promiseArray = tokens.map((token) => getTokenImage(token.fileName));
Promise.all(promiseArray)
.then(valueArray => {
this.setState(prevState => ({
...prevState,
urlImgs: valueArray
}))
})
.catch(err => console.log(err));
}
render() {
const { urlImgs } = this.state;
return (
<div class="welcome minted-tokens">
<h1 class="">Minted Tokens</h1>
<div class="row">
{this.props.tokens.map((token, key) => {
let name = '',
description = ''
if(token.to == this.props.account) {
token.name.length >= 17 ? name = token.name.slice(0,17) + '...' : name = token.name
token.description.length >= 28 ? description = token.description.slice(0,29) + '...' : description = token.description
let image = this.getTokenImage(token.fileName).then((result) => {return result})
console.log(image)
return (
<Link to={token.tokenURI} key={key}>
<div class='token-id'>{token.id}</div>
<div class="card card-width">
<div
class="card-img-top"
style={{
backgroundColor: '#'+token.color,
backgroundImage: `url(${urlImgs[key] ?? null})`
}}></div>
<h5 class="card-header" style={{
backgroundColor: '#'+token.color,
color: '#'+token.fontColor}}>{name}</h5>
<div class="card-body">
<p class="card-text">{description}</p>
<div class="foot-of-card">
<span class="row price-row">
<img src={ether} alt="ether" class="icon" />
<p class="card-text">{token.price}</p>
</span>
<p class="card-text datetime">{token.dateTime}</p>
</div>
</div>
</div>
</Link>
)
}
})}
</div>
</div>
);
}
}
export default MultipleMintedTokens;
if you have React with version > 16.8, I advice you to start using Stateless Components and Hooks. And read about the lifecycle of React and the render method here:

how do i toggle disabled between two buttons in react map

I have a list of candidates with two buttons hire and reject. when i press hire it should be disabled and reject stays enabled. When i press reject it should be disabled and hire must be enabled.
{result && result.map(appliedCandidate => {
if (joblist.id === appliedCandidate.jobid) {
return (
<div className="row pb-3">
<div className=" col-md-4 text-left font-weight-bold">
<p className={this.state.applystatus==="hire" ? "text- info" : "text-danger"}>
{appliedCandidate.firstName}
</p>
</div>
<div className="col-md-8">
<div className="row">
<div className="col-4">
<div className="back-btn">
<input id='hire' type='button' ref='hire' data-id={appliedCandidate.jobid} name={appliedCandidate.id} data-tag={appliedCandidate.phoneno} onClick={this.hireReject} className="btn btn-success card-btn-width" value='hire' />
</div>
</div>
<div className="col-4">
<div className="back-btn">
<input id='reject' type='button' ref='reject' data-id={appliedCandidate.jobid} name={appliedCandidate.id} data-tag={appliedCandidate.phoneno} onClick={this.hireReject} className="btn btn-danger card-btn-width" value='reject' />
</div>
</div>
<div className="col-4">
<div className="back-btn">
<Link to={{ pathname: '/individualchat', state: { name: appliedCandidate.firstName, jobid: appliedCandidate.jobid, id: appliedCandidate.id, Title: appliedCandidate.Title } }}>
<button type="button" className="btn btn-info">chat</button>
</Link>
</div>
</div>
</div>
</div>)
}
})}
hireReject = (event) => {
var dis = event.target.setAttribute('disabled','true')
const phoneno = event.target.getAttribute('data-tag');
const id = event.target.getAttribute('name');
const jobid = event.target.getAttribute('data-id');
const applystatus = event.target.value;
{ applystatus === 'hire' ? toastr.success('Successfully hired') : toastr.error('Successfully rejected') }
{ applystatus === 'hire' ? document.getElementById('reject').disabled = false : document.getElementById('hire').disabled = false }
this.setState({
jobid: jobid, id: id, candidatephoneno: phoneno, applystatus: applystatus
}, () => {
this.props.hireReject(this.state)
})
{return applystatus === 'hire' ? 'hired' : 'rejected'}
}
Consider separating the buttons and hiring/rejecting logic into its own component like the following so you can better handle the toggling.
Index.js
import React from "react";
import ReactDOM from "react-dom";
import Candidate from "./Candidate";
import "./styles.css";
class App extends React.Component {
state = {
text: ""
};
render() {
const candidates = [{ name: "Bob" }, { name: "Sam" }, { name: "Jessie" }];
return candidates.map(candidate => {
return <Candidate candidate={candidate} />;
});
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Candidate.js
import React from "react";
class Candidate extends React.Component {
state = {
hired: null
};
handleHire = () => {
this.setState({
hired: true
});
};
handleReject = () => {
this.setState({
hired: false
});
};
render() {
const hired = this.state.hired;
return (
<div>
<h4>{this.props.candidate.name}</h4>
<button
onClick={this.handleHire}
disabled={hired == null ? false : hired}
>
Hire
</button>
<button
onClick={this.handleReject}
disabled={hired == null ? false : !hired}
>
Reject
</button>
</div>
);
}
}
export default Candidate;
Here is a sandbox for your reference as well: https://codesandbox.io/s/zrlyq0l29m

How to perform the mutation of dropdowns status in ReactJS?

I want to make a change of state when an item is selected in my dropdown but a bug occurs that does not change without clicking again.
I know there are related questions but my code is a particular case.
The same thing happens to me as this image with my two dropdown.
I change "Mes" but it does not work until you return and select another value. There I just changed to "month 4".
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import format from 'date-fns/format';
import gql from 'graphql-tag';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { Link } from 'react-router-dom';
import { graphql } from 'react-apollo';
import '../../../node_modules/bootstrap/dist/css/bootstrap.css';
import './style.css';
import {
ButtonDropdown,
DropdownToggle,
DropdownMenu,
DropdownItem,
} from 'reactstrap';
import axios from 'axios';
import TituloSmall, { types } from '../TituloSmall';
const query0 = gql`
query postsDiaComoHoy($dia: Int!, $mes: Int!) {
posts(first: 2, dia: $dia, mes: $mes, categoria: 28) {
rows {
id
fecha_dia_hoy
imagen_intro
titulo
introtext
autor
fulltext
fulltext2
imagen_banner
categoria {
id
}
tags {
id
titulo
}
}
count
}
}
`;
const renderTagItem = item => {
const { id, titulo } = item;
return (
<Link key={id} to={`/tags/${id}/`}>
<div className="tag">{titulo}</div>
</Link>
);
};
const removeTagHtml = valor => valor.replace(/(<([^>]+)>)/g, '');
const removerTwitter = valor => valor.replace(/- #\w+/g, '');
let updated = 0;
let dates = format(Date(), 'D');
let month = format(Date(), 'M');
export class DayScreen extends Component {
constructor(props) {
super(props);
this.state = {
data: this.props,
currentIndex: 0,
sortItem1: month,
sortItem2: dates,
cantidadMeses: [
'Enero',
'Febrero',
'Marzo',
'Abril',
'Mayo',
'Junio',
'Julio',
'Agosto',
'Septiembre',
'Octubre',
'Noviembre',
'Diciembre',
],
diasMes: [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
],
};
}
state = {
items: [],
};
componentDidUpdate() {
this.getArticles();
updated = 0;
}
getArticles = async () => {
const changeArticles = `
{
posts(first: 3, dia: ${this.state.sortItem2}, mes: ${month}) {
rows {
id
fecha_dia_hoy
imagen_intro
titulo
introtext
autor
views
fulltext
fulltext2
imagen_banner
categoria {
id
}
tags{
id
titulo
}
}
count
}
}
`;
if (updated) {
try {
const response = await axios.get(
`http://localhost:4000/graphql/?query=${changeArticles}`,
) .then(response => {
this.setState(() => ({
items: response.data.data.posts.rows,
}));
let { data } = this.props;
const { items } = this.state;
data.posts.rows = items;
return response;
});
// Log the response so we can look at it in the console
// Set the data to the state
} catch (error) {
// If there's an error, set the error to the state
// this.setState(() => ({ error }));
console.log(error);
// console.log(this.state.error);
}
}
};
selectIndex = direction => {
const { currentIndex } = this.state;
const {
data: {
posts: { count },
},
} = this.props;
let nexIndex = currentIndex + direction;
nexIndex = nexIndex < 0 ? count - 1 : nexIndex;
nexIndex = nexIndex >= count ? 0 : nexIndex;
this.setState({ currentIndex: nexIndex });
};
monthSelected() {
const { sortItem1, sortItem2, dropdownOpen1 } = this.state;
this.setState({
dropdownOpen1: !dropdownOpen1,
});
if (dropdownOpen1) {
month = sortItem1;
dates = sortItem2;
updated = 1;
}
}
dateSelected() {
const { sortItem1, sortItem2, dropdownOpen2 } = this.state;
this.setState({
dropdownOpen2: !dropdownOpen2,
});
if (dropdownOpen2) {
month = sortItem1;
dates = sortItem2;
updated = 1;
}
}
onDiasChanged=(e)=>{
this.setState({sortItem2:[...e.currentTarget.innerHTML]});
}
onMesChanged=(e)=>{
this.setState({sortItem1:[...e.currentTarget.innerHTML]});
}
render() {
const { data } = this.props;
if (data.loading) {
return <div>Loading...</div>;
}
if (data.error) {
return <div>{data.error.message}</div>;
}
if (data.posts.rows.length <= 0) {
return <div>Nada que mostrar...</div>;
}
const {
data: {
posts: { rows },
},
} = this.props;
this.items = this.props.data.posts.rows;
const {
currentIndex,
sortItem1,
cantidadMeses,
sortItem2,
dropdownOpen1,
dropdownOpen2,
diasMes,
} = this.state;
const item = rows[currentIndex] ? rows[currentIndex] : rows[0];
const html = item.fulltext + item.fulltext2;
const image = `${process.env.REACT_APP_IMG_BASE}${item.imagen_intro ||
item.imagen_banner}`;
data.variables.mes = sortItem1;
data.variables.dia = sortItem2;
return (
<div className="containerDiaComoHoyNoticia">
<div className="box">
<span />
<span />
<div className="mesTexto">{cantidadMeses[sortItem1 - 1]}</div>
<div className="diaTexto">{sortItem2}</div>
</div>
<div>
<div className="textoContainer">
<div className="tituloDiaComoHoyNoticia">
{'BUSCA QUE OCURRIÓ EL DÍA QUE TU QUIERAS EN EL FÚTBOL'}
</div>
<div className="separatorLinea" />
<div className="listaMesDia">
<span className="circuloMesDia">1</span>
<span>Mes</span>
<ButtonDropdown
isOpen={dropdownOpen1}
toggle={() => {
this.monthSelected();
}}>
<DropdownToggle color="white" caret>
{sortItem1}
</DropdownToggle>
<DropdownMenu>
{cantidadMeses.map((items, i) => (
<DropdownItem
dropDownValue="Mes"
dropDownValue="Mes" onClick={this.onMesChanged}>
{i + 1}
</DropdownItem>
))}
</DropdownMenu>
</ButtonDropdown>
<span className="circuloMesDia">2</span>
<span>Dia</span>
<ButtonDropdown
isOpen={dropdownOpen2}
toggle={() => {
this.dateSelected();
}}>
<DropdownToggle caret>{sortItem2}</DropdownToggle>
<DropdownMenu>
{diasMes.map(i => (
<DropdownItem
dropDownValue="Mes" onClick={this.onDiasChanged}>
{i}
</DropdownItem>
))}
</DropdownMenu>
</ButtonDropdown>
</div>
</div>
</div>
{rows.map(itemArticulo => (
<div className="listaNoticiasContenido">
<img
alt={itemArticulo.titulo}
src={process.env.REACT_APP_IMG_BASE + itemArticulo.imagen_intro}
className="listaNoticiasImagen"
/>
<div className="rectanguloIconoPlay" />
<div className="contenidoArticulo">
<div className="tituloArticulo"><a href="#" onClick = {this.state.currentIndex = 1}>{itemArticulo.titulo}</a></div>
<div className="descripcionArticulo">
{removeTagHtml(itemArticulo.introtext)}
</div>
<div className="escritor">
<div className="nombreAutor">
<div>
<FontAwesomeIcon icon="user-circle" />
<span className="autorArticulo">
{removerTwitter(itemArticulo.autor) || 'Sin autor'}
</span>
<FontAwesomeIcon icon="eye" />
<span className="vistasTotalesArticulos">
{itemArticulo.views}
</span>
<FontAwesomeIcon icon="calendar" />
<span className="cantidadArticulosEscritos">
{itemArticulo.fecha_dia_hoy}
</span>
</div>
</div>
</div>
</div>
<div className="separadorArticulos" />
</div>
))}
<h2 className="tituloDescripcion">{item.titulo}</h2>
<div className="titlesContainer">
<TituloSmall
iconName="user-circle"
label={item.autor || 'Sin autor'}
/>
<TituloSmall
iconName="calendar"
label={format(item.fecha_dia_hoy, 'DD/MM/YYYY')}
/>
</div>
<div className="imageIntro">
<img className="imageDescription" src={image} alt={item.titulo} />
<div className="esquinaFigura">
<div className="boxWhite">
<span />
<span />
<div className="mesTextoBoxWhite">
{cantidadMeses[sortItem1 - 1]}
</div>
<div className="diaTextoBoxWhite">{sortItem2}</div>
</div>
</div>
</div>
<article dangerouslySetInnerHTML={{ __html: html }} />
<TituloSmall iconName="tags" label="Tags" type={types.BIG} />
{item.tags.map(itemsTags => renderTagItem(itemsTags))}
</div>
);
}
}
DayScreen.propTypes = {
data: PropTypes.shape({
loading: PropTypes.bool.isRequired,
currentSortItem: PropTypes.string.isRequired,
error: PropTypes.shape({ message: PropTypes.string }),
}).isRequired,
};
DayScreen.defaultProps = {};
const queryOptions = {
options: () => ({
variables: {
dia: dates,
mes: month,
},
}),
};
export default graphql(query0, queryOptions)(DayScreen);
I want the change of state to be made without pressing double click and the delay that I showed in the image occurs.
Thanks for your help!
You have directly mutated the state and not setting the state using setState method so your render method will not be called at the time of selection and because of it, you will not getting the change immediately once your render method will be call you will get those changes. so your dropdown implementation should be like.
onMessChanged=(e)=>{
this.setState({sortItem2:e.currentTarget.innerHTML});
}
<DropdownMenu>
{diasMes.map(i => (
<DropdownItem
dropDownValue="Mes"
onClick={this.onMessChanged}>
{i}
</DropdownItem>
))}
</DropdownMenu>
As here you are using the reactstrap so it might be possible that your event target may be different so you have to use currentTarget
the currentTarget refers to the element that the event listener directly attached to while the target still refers to the specific element where we clicked.
First error I see is that you can not set the state directly as this.state.sortItem = e.target.innerHTMLInstead you have to use this.setState({ sortItem: e.targetinnerHTML })
Y por favor, intenta no usar Spanglish xD es una mala práctica.

Resources