Issue with Increment and Decrement Component - javascript

I'm pretty new to react, and I am trying to make an Increment and decrement component in in my cart screen. The issue that I'm having is that I'm struggling to get it to work and to only add as many items as are contained within the countInStock stored in my database.
Below, I have included what i have so far. I would really appreciate any help or advice on how to do this.
Note: The handlers associated with the increment and decrement component are removeItemFromCart and addToCart
CartScreen.js
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link } from 'react-router-dom';
import { addToCart, removeFromCart, removeItemFromCart } from '../actions/cartActions';
import MessageBox from '../components/MessageBox';
export default function CartScreen(props) {
const productId = props.match.params.id;
const qty = props.location.search
? Number(props.location.search.split('=')[1])
: 1;
const cart = useSelector((state) => state.cart);
const { cartItems } = cart;
const dispatch = useDispatch();
const addToCartHandler = (productId, qty) =>{
useEffect(() => {
if (productId && countInStock > qty) {
dispatch(addToCart(productId, qty));
}
}, [dispatch, productId, qty]);
}
const removeItemFromCartHandler = (id) => {
dispatch(removeItemFromCart(id));
};
const removeFromCartHandler = (id) => {
// delete action
dispatch(removeFromCart(id));
};
const checkoutHandler = () => {
props.history.push('/signin?redirect=shipping');
};
return (
<div className="row top">
<div className="col-2">
<h1>Shopping Cart</h1>
{cartItems.length === 0 ? (
<MessageBox>
Cart is empty. <Link to="/body">Go Shopping</Link>
</MessageBox>
) : (
<ul>
{cartItems.map((item) => (
<li key={item.product}>
<div className="row">
<div className="min-30">
<Link to={`/product/${item.product}`}>{item.name}</Link>
</div>
<div>
<button
onClick={() =>
addToCartHandler(item, item.qty - 1)
}
variant="light"
disabled={item.qty === 1}
>
<i className="fas fa-minus-circle"></i>
</button>{' '}
<span>{item.qty}</span>{' '}
<button
variant="light"
onClick={() =>
removeItemFromCartHandler(item, item.qty + 1)
}
disabled={item.qty === item.countInStock}
>
<i className="fas fa-plus-circle"></i>
</button>
</div>
<div>
<button
type="button"
onClick={() => removeFromCartHandler(item.product)}
>
Delete
</button>
</div>
</div>
</li>
))}
</ul>
)}
</div>
</div>
);
}
CartActions.js
import Axios from 'axios';
import {
CART_ADD_ITEM,
CART_REMOVE_ITEM,
CART_REMOVER_ITEM,
CART_SAVE_SHIPPING_ADDRESS,
CART_SAVE_PAYMENT_METHOD,
} from '../constants/cartConstants';
export const addToCart = (productId, qty) => async (dispatch, getState) => {
const { data } = await Axios.get(`/api/products/${productId}`);
dispatch({
type: CART_ADD_ITEM,
payload: {
name: data.name,
image: data.image,
price: data.price,
profit: data.profit,
countInStock: data.countInStock,
product: data._id,
seller: data.seller,
qty,
},
});
localStorage.setItem('cartItems', JSON.stringify(getState().cart.cartItems));
};
export const removeItemFromCart = (productId) => (dispatch, getState) => {
dispatch({ type: CART_REMOVER_ITEM, payload: productId });
localStorage.setItem('cartItems', JSON.stringify(getState().cart.cartItems));
};
CartReducers.js
import {
CART_ADD_ITEM,
CART_EMPTY,
CART_REMOVE_ITEM,
CART_REMOVER_ITEM,
CART_SAVE_PAYMENT_METHOD,
CART_SAVE_SHIPPING_ADDRESS,
} from '../constants/cartConstants';
export const cartReducer = (state = { cartItems: [] }, action) => {
switch (action.type) {
case CART_ADD_ITEM:
const item = action.payload;
const existItem = state.cartItems.find((x) => x.product === item.product);
if (existItem) {
return {
...state,
cartItems: state.cartItems.map((x) =>
x.product === existItem.product ? item : x
),
};
} else {
return { ...state, cartItems: [...state.cartItems, item] };
}
case CART_REMOVE_ITEM:
return {
...state,
cartItems: state.cartItems.filter((x) => x.product !== action.payload),
};
case CART_REMOVER_ITEM:
return {
...state,
cartItems: state.cartItems.filter((x) => x.product !== action.payload),
};
case CART_SAVE_SHIPPING_ADDRESS:
return { ...state, shippingAddress: action.payload };
case CART_SAVE_PAYMENT_METHOD:
return { ...state, paymentMethod: action.payload };
case CART_EMPTY:
return { ...state, cartItems: [] };
default:
return state;
}
};

Related

Error in Cart Page while updating product quantity

Please help me with my REACT problem. On the cart page, while I try to increase the quantity or decrease the number of quantity products, I get this type of error. I have attached the error in the Image. Please see the Image.
Here's My Code:
Cart.js. Trying to update the product quantity on the cart page. But getting errors as you see in the picture.
import React, { Fragment } from 'react';
import "./Cart.css";
import CartItemCard from "./CartItemCard";
import { useSelector, useDispatch } from 'react-redux'
import { addItemsToCart } from '../../action/cartAction';
const Cart = () => {
const dispatch = useDispatch();
const { cartItems } = useSelector((state) => state.cart);
const increaseQuantity = (id, quantity, stock) => {
const newQty = quantity + 1 ;
if(stock <= quantity) {
return;
}
dispatch(addItemsToCart(id, newQty));
};
const decreaseQuantity = (id, quantity) => {
const newQty = quantity - 1 ;
if (1 >= quantity) {
return;
}
dispatch(addItemsToCart(id, newQty));
};
return (
<Fragment>
<div className="cartPage">
<div className="cartHeader">
<p>Product</p>
<p>Quantity</p>
<p>Subtotal</p>
</div>
{cartItems && cartItems.map((item) => (
<div className="cartContainer" key={item.product} >
<CartItemCard item={item} />
<div className="cartInput">
<button onClick={() => decreaseQuantity(item.product, item.quantity)} > - </button>
<input type="number" readOnly value={item.quantity} />
<button onClick={() => increaseQuantity(item.product, item.quantity, item.stock)} > + </button>
</div>
<p className="cartSubtotal">{`$${item.price*item.quantity}`}</p>
</div>
))}
<div className="cartGrossTotal">
<div></div>
<div className="cartGrossTotalBox">
<p>Gross Total</p>
<p>{`$600`}</p>
</div>
<div></div>
<div className="checkOutBtn">
<button>Check Out</button>
</div>
</div>
</div>
</Fragment>
);
};
export default Cart;
Here's the Action:
cartAction.js
import {ADD_TO_CART} from "../constants/cartConstants";
import axios from "axios";
export const addItemsToCart = (id, quantity) => async (dispatch,getState) => {
const { data } = await axios.get(`/api/v1/product/${id}`);
dispatch({
type: ADD_TO_CART,
payload: {
product: data.product._id,
name: data.product.name,
price: data.product.price,
image: data.product.images[0].url,
stock: data.product.Stock,
quantity,
},
});
localStorage.setItem("cartItems",JSON.stringify(getState().cart.cartItems));
};
Cart Reducer Code.
CartReducer:
import {ADD_TO_CART} from "../constants/cartConstants";
export const cartReducer = (state = { cartItems: [] }, action ) => {
switch (action.type) {
case ADD_TO_CART:
const item = action.payload;
const isItemExist = state.cartItems.find(
(i) => i.product === item.product
);
if(isItemExist) {
return {
...state,
cartItems: state.cartItems.map((i) =>
i.product === isItemExist.product ? true : i
),
};
} else {
return{
...state,
cartItems: [...state.cartItems, item],
};
}
default:
return state;
}
};
React Store.
Store:
import { createStore,combineReducers,applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "#redux-devtools/extension";
import { productDetailsReducer, producReducer } from "./reducers/productReducer";
import { forgotPasswordReducer, profileReducer, userReducer } from "./reducers/userReducer";
import { cartReducer } from "./reducers/cartReducer";
const reducer = combineReducers({
products: producReducer,
productDetails: productDetailsReducer,
user: userReducer,
profile: profileReducer,
forgotPassword: forgotPasswordReducer,
cart: cartReducer,
});
let initialState = {
cart: {
cartItems: localStorage.getItem("cartItems")
? JSON.parse(localStorage.getItem("cartItems"))
: [],
}
};
const middleware = [thunk]
const store = createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware)
));
export default store;
Thanks, everyone for your comments and view.
I got the solution. Here's in the CartReducer code I added true instead of the item. this is the solution. Thank you.
if(isItemExist) {
return {
...state,
cartItems: state.cartItems.map((i) =>
i.product === isItemExist.product ? item : i
),
}
};

How to combine the previous data with the new data in React and display it at the same time

I'm newbie to Reactjs. The problem I'm encountered:
I want to capture and display new data when I scroll down
I take the data every time I go down and save it in Redax, but because it has pagination, it only shows 10 and does not add to the previous one.
this is my code
import { memo, useEffect, useState } from "react";
import Collapse from "#kunukn/react-collapse";
import CustomHeader from "SharedComponents/CustomHeader";
import InfiniteScroll from "react-infinite-scroller";
import "./styles.scss";
import Item from "./Components/Item/Item";
import { dispatchItemToRedux, getListServiceTransactions } from "Redux/Actions";
import { useDispatch, useSelector } from "react-redux";
import CircleLoading from "SharedComponents/Loading/CircleLoading";
import EmptyList from "./Components/EmptyList";
import ReducerTypes from "Redux/Types/ReducerTypes";
const Page = () => {
const ListServiceTransactions = useSelector(
(state) => state.app.ListServiceTransactions
);
const dispatch = useDispatch();
const [loading, setLoading] = useState(false);
const [type, setType] = useState("");
const [open, setOpen] = useState(false);
const transactionItems = ListServiceTransactions?.items?.data;
const transactionPagination = ListServiceTransactions?.items;
const typeFilters = ListServiceTransactions?.type_filters;
const resultFilters = [];
useEffect(() => {
setLoading(true);
dispatch(getListServiceTransactions(null, 1));
setLoading(false);
}, []);
const handleLoadMore = () => {
const {
total,
per_page,
current_page,
next_page_url,
} = transactionPagination;
if (total > per_page && next_page_url) {
dispatch(getListServiceTransactions(type, current_page + 1)).then(
(res) => {
transactionItems.push(res);
}
);
}
};
const clickFilter = (value) => {
dispatch(getListServiceTransactions(type, 1));
setLoading(true);
setOpen(false);
setLoading(false);
};
return (
<div className="list-transactions-screen">
<CustomHeader title="لیست تراکنش های خدمات" />
<div className="p-4">
<div className="box-filter-tarnsactions mb-3">
<div className="button-open-collapse">
<div
className={`box-value-filter ${
width && open == true ? "animation-filter" : null
}`}
>
{typeTitle}
</div>
<button className="btn-filter-tarnsactions" onClick={OpenBox}>
||
</button>
</div>
<Collapse
isOpen={open}
transition="height 400ms cubic-bezier(0.4, 0, 0.2, 1)"
>
{resultFilters
? resultFilters.map((typeItem) => {
return (
<p
className="m-0 my-2 p-2 bg-white border rounded"
onClick={() => clickFilter(typeItem)}
>
{typeItem.label}
</p>
);
})
: null}
</Collapse>
</div>
<div className="all-tarnsactions-list">
{!loading ? (
transactionItems?.length > 0 ? (
transactionItems ? (
<InfiniteScroll
pageStart={1}
loadMore={handleLoadMore}
hasMore={true}
threshold={200}
loader={
!transactionPagination.next_page_url &&
transactionPagination.next_page_url === null ? null : (
<CircleLoading key={0} />
)
}
>
{transactionItems.map((item) => {
return <Item item={item} />;
})}
</InfiniteScroll>
) : (
<CircleLoading />
)
) : (
<EmptyList />
)
) : (
<CircleLoading />
)}
</div>
</div>
</div>
);
};
export default memo(Page);
enter code here
this is my action
function getListServiceTransactions(
type: any = null,
page: number = 1,
) {
return function (dispatch: ReduxDispatch, getState: ReduxGetState) {
return new Promise(function (resolve, reject) {
const params = {
type: type,
page: page,
};
axios({
method: "GET",
baseURL: Api.PUBLIC_API_V2,
url: "other-services/transaction/list",
params: params,
})
.then((res) => {
dispatch(
dispatchItemToRedux({
type: ReducerTypes.LIST_SERVICE_TRANSACTIONS,
payload: res.data.data,
})
);
resolve(res);
console.log("raft to action v req zad v in response", res);
})
.catch((err) => {
reject(err);
});
});
};
}
this is my reducer
function appReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case ReducerTypes.SET_STEPPER:
return { ...state, activeStep: action.payload };
case ReducerTypes.TOGGLE_BUTTON_SPINNER:
return { ...state, showButtonSpinner: action.payload.value };
case ReducerTypes.LOCATION_PERMISSION:
return { ...state, locationPermission: action.payload };
case ReducerTypes.TOGGLE_LOADING:
return { ...state, showLoading: action.payload.value };
case ReducerTypes.TOGGLE_LOADING_EVERLAY:
return { ...state, showLoadingEverlay: action.payload.value };
case ReducerTypes.TOGGLE_SHOW_SIDEBAR:
return { ...state, showSidebar: action.payload.value };
case ReducerTypes.TOGGLE_SHOW_NOTIFICATION:
return { ...state, showNotification: action.payload.value };
case ReducerTypes.TOAST_MESSAGE_MODAL:
return {
...state,
toastMessage: { ...action.payload.toastMessage },
};
case ReducerTypes.MAIN_PAGE_DATA:
return {
...state,
mainPageState: action.payload,
};
case ReducerTypes.CAMPAIGN_BUSINESSES_DATA:
return {
...state,
campaignBusinessesData: action.payload,
};
case ReducerTypes.CAMPAIGN_BUSINESSES_ITEMS:
return {
...state,
campaignBusinessesItems: action.payload,
};
case ReducerTypes.LOADING_PINS:
return { ...state, loadingPins: action.payload.value };
case ReducerTypes.APP_CATEGORIES:
return {
...state,
categories: action.payload.categories,
structuredCategories: action.payload.structuredCategories,
};
case ReducerTypes.APP_DISCOUNTED_BUSINESS:
return {
...state,
discountedBusinesses: action.payload.discountedBusinesses,
};
**case ReducerTypes.LIST_SERVICE_TRANSACTIONS:
return {
...state,
ListServiceTransactions: action.payload,
};**
case ReducerTypes.FAQ_LIST:
return { ...state, faqList: action.payload.faqList };
default:
return state;
}
}
export default appReducer;
I just need to put the binaries together and handle the rest of the work myself
I think what you need to do is to merge the old state together with the new one in the reducer, I am not exactly sure which key you mean, but I assume ListServiceTransactions, if that's the case, then in your reducer file, it should be:
**case ReducerTypes.LIST_SERVICE_TRANSACTIONS:
return {
...state,
// Combining previous state with the new one
ListServiceTransactions: [
...state.ListServiceTransactions,
...action.payload
],
};**

Category.js:55 Uncaught TypeError: categories is not iterable

I want to display Categories and Category Children in the admin dashboard and the terminal gives no error. But the page consol renders Category.js:55 Uncaught TypeError: categories is not iterable
Uncaught (in promise) TypeError: categories is not iterable
Category Component:
import React, { useEffect ,useState } from 'react';
import { Container, Row, Col ,Modal ,Button} from 'react-bootstrap';
import Layout from '../../components/Layout/Layout'
import { useDispatch, useSelector } from 'react-redux'
import { getAllCategory } from '../../actions'
import Input from '../../components/UI/Input/Input'
import {addCategory} from '../../actions/category.actions'
const Category = () => {
const category = useSelector(state => state.category)
const [categoryName , setCategoryName] =useState('')
const [parentCategoryId , setParentCategoryId] =useState('')
const [categoryImage , setCategoryImage] =useState('')
const dispatch = useDispatch()
useEffect(() => {
console.log('Category.js')
dispatch(getAllCategory())
}, [])
const [show, setShow] = useState(false);
const handleClose = () => {
const form = new FormData()
// const cat ={
// categoryName,
// parentCategoryId,
// categoryImage
// }
form.append('name',categoryName)
form.append('parentId',parentCategoryId)
form.append('categoryImage',categoryImage)
dispatch(addCategory(form))
// console.log('cat',cat)
setShow(false);
}
const handleShow = () => setShow(true);
const renderCategories = (categories) => {
let myCategories = []
for (let category of categories) {
myCategories.push(
<li key={Math.random()}>
{category.name}
{category.children.length > 0 ? (<ul>{renderCategories(category.children)}</ul>) : null}
</li>
)
}
return myCategories;
}
const createCategoryList=(categories,options=[])=>{
for(let category of categories) {
options.push({value: category._id , name: category.name})
if(category.children.length > 0){
createCategoryList(category.children, options)
}
}
return options;
}
const handelCategoryImage =(e)=>{
setCategoryImage(e.target.files[0])
}
return (
<>
<Layout sidebar>
<Container>
<Row>
<Col md={12}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<h3>Category</h3>
<button onClick={handleShow}>Add</button>
</div>
</Col>
</Row>
<Row>
<Col md={12}>
<ul>
{renderCategories(category.categories)}
</ul>
</Col>
</Row>
</Container>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Add New Category</Modal.Title>
</Modal.Header>
<Modal.Body>
<Input
value={categoryName}
placeholder={'Category Name'}
onChange={(e)=>setCategoryName(e.target.value)}
/>
<select className="form-control" onChange={(e)=>setParentCategoryId(e.target.value)} value={parentCategoryId}>
<option>Select Category</option>
{
createCategoryList(category.categories).map(option =>
<option key={option.value} value={option.value}>{option.name}</option>)
}
</select>
<input type='file' name='categoryImage' onChange={handelCategoryImage}/>
</Modal.Body>
<Modal.Footer>
<Button variant="primary" onClick={handleClose}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</Layout>
</>
);
};
export default Category;
Category.action.js
import axios from "axios"
import axiosInstance from "../helpers/axios"
import {categoryConstants} from './constants'
export const getAllCategory =()=>{
return async dispatch => {
dispatch({type: categoryConstants.GET_ALL_CATEGORIES_REQUEST})
const res =await axios.get('http://localhost:2000/api/category/getcategory')
console.log("res",res)
if(res.status === 200) {
// const {categoryList} = res.data
// console.log("categoryList",categoryList)
dispatch({
type:categoryConstants.GET_ALL_CATEGORIES_SUCCESS,
payload: {category:res.data.category}
})
}else{
dispatch({
type: categoryConstants.GET_ALL_CATEGORIES_FAILURE,
payload: {error: res.data.error}
})
}
}
}
export const addCategory =(form) => {
const token =window.localStorage.getItem('token')
return async dispatch => {
dispatch({ type: categoryConstants.ADD_NEW_CATEGORY_REQUEST})
const res = await axios.post('http://localhost:2000/api/category/create',form,{headers:{
'Authorization':token ? `Bearer ${token}` :''
}})
if(res.status === 200){
dispatch({
type: categoryConstants.ADD_NEW_CATEGORY_SUCCESS,
payload:res.data.category
})
}else{
dispatch({
type: categoryConstants.ADD_NEW_CATEGORY_FAILURE,
payload:res.data.error
})
}
console.log("res", res)
}
}
category.reducer.js
import {categoryConstants} from '../actions/constants'
const initState ={
categories:[],
loading:false,
error:null,
}
const buildNewCategories =(categories,category)=>{
let myCategories=[]
for(let cat of categories){
myCategories.push({
...cat,
children: cat.children && cat.children.length > 0 ? buildNewCategories(cat.children,category):[]
})
}
return myCategories;
}
export default (state = initState , action)=>{
switch(action.type){
case categoryConstants.GET_ALL_CATEGORIES_SUCCESS:
state={
...state,
categories: action.payload.categories
}
break;
case categoryConstants.ADD_NEW_CATEGORY_REQUEST:
state={
...state,
loading: true,
}
break;
case categoryConstants.ADD_NEW_CATEGORY_SUCCESS:
const updatedCategories=buildNewCategories(state.categories, action.payload.category)
console.log('updated categoires', updatedCategories);
state={
...state,
categories:updatedCategories,
loading: false,
}
break;
case categoryConstants.ADD_NEW_CATEGORY_FAILURE:
state={
...initState,
}
break;
}
return state;
}
When you call const category = useSelector(state => state.category) to get category , you was not sure whether or not category has been fetched successfully yet ( focus on the calling getAllCategory() on your useEffect ).
You just need to check before iterate categories , and some refactor your code like this is fine:
const renderCategories = (categories) => {
if(!Array.isArray(categories)) return null
return categories.map((category, i) => (
<li key={`category-${i}`}>
{category.name}
{Array.isArray(category.children) && category.children.length > 0 ? (
<ul>{renderCategories(category.children)}</ul>
) : null}
</li>)
)
}
Also you can wrap your function renderCategories with useCallback to make more effective
import { useCallback } from 'react'
const renderCategories = useCallback((categories) => {
if(!Array.isArray(categories)) return null
return categories.map((category, i) => (
<li key={`category-${i}`}>
{category.name}
{Array.isArray(category.children) && category.children.length > 0 ? (
<ul>{renderCategories(category.children)}</ul>
) : null}
</li>)
)
}, [])

How to add product to cart by quantity? - Context API - React

I have been able to get the functionality of my state management to the point where I can add to the basket and remove the item from the basket, however I want to be able to check if item is already in the basket and if so just update the quantity and also remove by quantity rather than the basketItem removing entirely even if there was more than one of it.
If you look at the basket when adding items it just adds more basket items with each of them displaying total quantity - but I just want the quantity to update in the basketItem when there is more than one.
for reference I have not yet implemented the CHANGE_CART_QUANTITY reducer as I am having difficulty working out how to use it in my application
code sandbox here
code below:
CartContext.js
import { createContext, useContext, useReducer } from "react";
import { CartReducer } from "./CartReducer";
import { products } from "../../pages/ProductDetailsPage";
const Cart = createContext();
const Context = ({ children }) => {
const [state, dispatch] = useReducer(CartReducer, {
products: products,
cart: [],
});
// const [productState, productDispatch] = useReducer(productReducer, {
// byStock: false,
// byFastDelivery: false,
// byRating: 0,
// searchQuery: "",
// });
// console.log(productState);
return (
<Cart.Provider value={{ state, dispatch }}>
{children}
</Cart.Provider>
);
};
export const CartState = () => {
return useContext(Cart);
};
export default Context;
CartReducer.js
import {ADD_TO_CART, CHANGE_CART_QUANTITY, REMOVE_FROM_CART} from '../Types'
export const CartReducer = (state, action) => {
switch (action.type) {
case ADD_TO_CART: {
return {
...state,
cart: [...state.cart, { ...action.payload, qty : 1}]
};
}
case REMOVE_FROM_CART: {
return {
...state,
cart: state.cart.filter((c) => c.id !== action.payload.id),
};
}
case CHANGE_CART_QUANTITY:
return {
...state,
cart: state.cart.filter(c => c.id === action.payload.id ? c.qty = action.payload.qty : c.qty )
}
default:
return state
}
}
Product.js
import React, { useContext } from 'react'
import { QuantityButtonDiv } from './QuantityButtonDiv'
import {BasketItem} from './BasketItem'
import { CartContext, CartState } from '../context/cart/CartContext'
import { ADD_TO_CART, REMOVE_FROM_CART } from '../context/Types'
export const Product = ({product}) => {
// const {addToCart, cartItems, removeItem } = useContext(CartContext)
const { state: {cart}, dispatch } = CartState();
return (
<div>
<div className="image-div">
<img style={{height: "100%", width: "100%"}} src={product.image}/>
</div>
<div className="details-div">
<h1>{product.title}</h1>
<span>
{product.description}
</span>
<span className="price">
£ {product.price}
</span>
<div className="stock-div">
{product.stock} in stock
</div>
<QuantityButtonDiv/>
{cart.some((p) => p.id === product.id) ? (
//checking to see if item is in cart if so remove from cart button appears
<button onClick={() => dispatch({
type: REMOVE_FROM_CART,
payload: product,
})} className="remove-button">
Remove From Cart
</button>
) : (
<></>
)}
<button onClick={() => dispatch({
type: ADD_TO_CART,
payload: product,
})} disable={!product.stock} className="add-to-cart-button">
{!product.stock ? "Out Of Stock" : "Add To Cart"}
</button>
</div>
</div>
)
}
BasketItem.js
import React, { useContext } from 'react'
import image from '../assets/image.png'
// import { QuantityButtonDiv } from '../components/QuantityButtonDiv'
import plusButtonImage from '../assets/vector+.png'
import subtractButtonImage from '../assets/vector.png'
import { CartState } from '../context/cart/CartContext'
import { ADD_TO_CART, REMOVE_FROM_CART } from '../context/Types'
import CustomizedSelect from './SelectInput'
export const BasketItem = ({item}) => {
// const { cartItems, removeItem } = useContext(CartContext);
const {
state: { cart },
dispatch,
} = CartState();
return (
<div className="basket-item">
<div className="title-div">
<span>
{item.title}
</span>
</div>
<div className="image-div">
<img style={{height: "100%", width: "100%"}} src={image}/>
</div>
<div className="price-div">
<span>
£{item.price}
</span>
</div>
<div className="basket-quantity-div">
<button onClick={() => dispatch({
type: REMOVE_FROM_CART,
payload: item,
})} className="subtract-btn">
<img src={subtractButtonImage}/>
</button>
<span className="quantity-value">
{cart.length}
</span>
<button onClick={() => dispatch({
type: ADD_TO_CART,
payload: item,
})} className="add-btn">
<img src={plusButtonImage}/>
</button>
</div>
<div className="total-div">
£{cart.reduce((amount, item) => item.price + amount, 0)}
</div>
</div>
)
}

Cart total is showing NAN instead of Total price

So the problem I am currently facing is this. I have a Cart logic located in the CartContext. Everything works except the total number of prices it is displaying NAN. Here is the link to the CodeSandbox for a better understanding https://codesandbox.io/s/frosty-sound-5y7pg?file=/src/CartItem.js:1486-1494.Please comment if something is wrong with sandbox
import React from "react";
function getCartFromLocalStorage() {
return localStorage.getItem("cart")
? JSON.parse(localStorage.getItem("cart"))
: [];
}
const CartContext = React.createContext();
function CartProvider({ children }) {
const [cart, setCart] = React.useState(getCartFromLocalStorage());
const [total, setTotal] = React.useState(0);
const [cartItems, setCartItems] = React.useState(0);
React.useEffect(() => {
localStorage.setItem("cart", JSON.stringify(cart));
let newTotal = cart.reduce((total, cartItem) => {
return (total += cartItem.amount * cartItem.price);
}, 0);
newTotal = parseFloat(newTotal.toFixed(2));
setTotal(newTotal);
// cart items
let newCartItems = cart.reduce((total, cartItem) => {
return (total += cartItem.amount);
}, 0);
setCartItems(newCartItems);
}, [cart]);
// global functions
const removeItem = id => {
setCart([...cart].filter(item => item.id !== id));
};
const increaseAmount = id => {
const newCart = [...cart].map(item => {
return item.id === id
? { ...item, amount: item.amount + 1 }
: { ...item };
});
setCart(newCart);
};
const decreaseAmount = (id, amount) => {
if (amount === 1) {
removeItem(id);
return;
} else {
const newCart = [...cart].map(item => {
return item.id === id
? { ...item, amount: item.amount - 1 }
: { ...item };
});
setCart(newCart);
}
};
const addToCart = book => {
const { id, image, by, bookName,RegularPrice } = book;
const item = [...cart].find(item => item.id === id);
if (item) {
increaseAmount(id);
return;
} else {
const newItem = { id, image, by, bookName, RegularPrice, amount: 1 };
const newCart = [...cart, newItem];
setCart(newCart);
}
};
const clearCart = () => {
setCart([]);
};
return (
<CartContext.Provider
value={{
cart,
cartItems,
total,
removeItem,
increaseAmount,
decreaseAmount,
addToCart,
clearCart
}}
>
{children}
</CartContext.Provider>
);
}
export { CartContext, CartProvider };
Cart Item
import React, { useContext } from "react";
import {Link, useHistory } from 'react-router-dom'
import { CartContext } from "../../context/cart";
import { FaAngleDown, FaAngleUp } from "react-icons/fa";
import Checkout from "./Checkout";
export default function CartItem({ id, image,bookName, RegularPrice, by, amount }) {
const { removeItem, increaseAmount, decreaseAmount } = React.useContext(
CartContext
);
return (
<div id={id} className="cart__item">
<img className='cart__image' src={image} />
<div className='cart__itemdesc'>
<h4>{bookName}</h4>
<h6 className='cart__by'>By: {by}</h6>
<button
className="cart__removebtn"
onClick={() => {
removeItem(id);
}}
>
Remove
</button>
<div>
<button
className="cart-btn amount-btn"
onClick={() => {
increaseAmount(id);
}}
>
<FaAngleUp />
</button>
<p className="item-amount">{amount}</p>
<button
className="cart-btn amount-btn"
onClick={() => {
decreaseAmount(id, amount);
}}
>
<FaAngleDown />
</button>
</div>
</div>
<span className='circle'><span className='circleone'></span></span>
<span className='cart__regular'>{RegularPrice}</span>
<div>
<Checkout />
</div>
</div>
);
}
Checkout
import React,{useContext} from 'react'
import { CartContext } from '../../context/cart'
import {Link, useHistory } from 'react-router-dom'
import EmptyCart from './EmptyCart';
const Checkout = () => {
const history = useHistory()
const {cart, total} = useContext(CartContext)
if (cart.length === 0) {
return <EmptyCart />;
}
return (
<div className='checkout'>
<h2>Summary</h2>
<h2>Subtotal : ${total}</h2>
<Link to='/stripecontainer' className='checkout__btnOne'>Proceed to Checkout</Link>
</div>
)
}
export default Checkout

Categories

Resources