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
),
}
};
Related
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;
}
};
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>
)
}
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
I am trying to achieve cart functionality, but I want to save the cart on the refresh of the page. Anyway, the problem is this I have Context Api and cart logic (add to cart, removeitem, amount, setCartItem) so when I try to add an item to the cart, nothing happens, and when I check the cart nothing is in there. I know this question has a lot of code, and the mistake could be anywhere, if someone could help I would be really grateful.
Cart Context
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);
let newCartItems = cart.reduce((total, cartItem) => {
return (total += cartItem.amount);
}, 0);
setCartItems(newCartItems);
}, [cart]);
const removeItem = key => {
setCart([...cart].filter(item => item.key !== key));
};
const addToCart = book => {
const { key, image, bookName, by } = book;
const item = [...cart].find(item => item.key === key);
}
const clearCart = () => {
setCart([]);
};
return (
<CartContext.Provider
value={{
cart,
cartItems,
total,
removeItem,
addToCart,
clearCart
}}
>
{children}
</CartContext.Provider>
);
}
export { CartContext, CartProvider };
Books
import React,{useContext} from 'react'
import { CartContext } from '../../context/cart';
import HoverBooks from './HoverBooks';
import { useHistory } from "react-router-dom";
const Books = ({category}) => {
const {addToCart }= useContext(CartContext)
return (
<div className='books__main'>
{category.slice(0, 5).map((book) => {
return(
<>
<HoverBooks
key={book.key}
{...book}
/>
<div className='book__content'>
<li>{book.bookName}</li>
<h4>By{book.by}</h4>
<h4>Narreted by:{book.Narreted}</h4>
<h4>Length: {book.length}</h4>
<h4>Release Date: {book.ReleaseDate}</h4>
<h4>Language: {book.Language}</h4>
<h4>{book.rating}</h4>
</div>
<div>
<span>Regular Price: {book.RegularPrice}</span>
<button onClick={() => {
addToCart(book);
}}
>Add to cart</button>
</div>
</>
)})}
</div>
)
}
export default Books
Cart
import React from "react";
import { Link } from "react-router-dom";
import EmptyCart from "./EmptyCart";
import CartItem from './CartItem'
import { UserContext } from "../../context/user";
import { CartContext } from "../../context/cart";
import './Cart.css'
export default function Cart() {
const { cart, total } = React.useContext(CartContext);
const { user } = React.useContext(UserContext);
if (cart.length === 0) {
return <EmptyCart />;
}
return (
<div className="cart__items">
<h2>your cart</h2>
{cart.map(item => {
return <CartItem key={item.key} {...item} />;
})}
<h2>total : ${total}</h2>
{user.token ? (
<Link to="/cart" className="btn">
</Link>
) : (
<Link to="/login" className="btn">
login
</Link>
)}
</div>
);
}
Cart Item
import React, { useContext } from "react";
import { CartContext } from "../../context/cart";
export default function CartItem({ key, image,bookName, amount }) {
const {removeItem} = useContext(CartContext)
return (
<div className="cart__item">
<img src={image} />
<div>
<h4>{bookName}</h4>
<h5>${by}</h5>
<button
className="cart__removebtn"
onClick={() => {
removeItem(key);
}}
>
remove
</button>
</div>
<p className="item__amount">{amount}</p>
</div>
);
}
Cart Link
import React from "react";
import { Link } from "react-router-dom";
import {FiShoppingCart} from 'react-icons/fi'
import { CartContext } from "../../context/cart";
export default function CartLink() {
const { cartItems } = React.useContext(CartContext);
return (
<div className="cartlink__container">
<Link to="/cart">
<FiShoppingCart />
</Link>
<span className="cartlink__total">{cartItems}</span>
</div>
);
}
Home
import React,{useState, useEffect, useContext} from 'react'
import './Home.css'
import Books from './Books'
import { BookContext } from "../../context/books";
const Home = () => {
const {data, handleSelectCategory, currentSelectedCategory }
=useContext(BookContext)
return (
<div className='books__container' >
<h1 className='categories'>Categories</h1>
{Object.keys(data).map((key, index)=>{
let books = data[key];
return (
<>
<span key={key} onClick={() => handleSelectCategory(key)}
className='books__list' >
{books[0].category}
</span>
</>
);})}
<Books category={currentSelectedCategory} />
</div>
)
}
export default Home
book
import React, {useState, useEffect} from 'react'
import URL from '../utilis/URL';
const BookContext = React.createContext();
export default function BooksProvider({ children }) {
const [data, setData] = useState([])
const [currentSelectedCategory, setCurrentSelectedCategory] = useState([]);
const handleSelectCategory = (category) => {
setCurrentSelectedCategory(data[category]);
};
const fetchData = async () => {
const response = await fetch(URL);
const result = await response.json();
console.log(result)
setCurrentSelectedCategory(result[Object.keys(result)[0]]);
setData(result);
};
useEffect(()=>{
fetchData();
},[])
return (
<BookContext.Provider value={{ data, handleSelectCategory, setCurrentSelectedCategory, currentSelectedCategory }}>
{children}
</BookContext.Provider>
);
}
export {BookContext, BooksProvider}
function onUpdate() {
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);
let newCartItems = cart.reduce((total, cartItem) => {
return (total += cartItem.amount);
}, 0);
setCartItems(newCartItems);
}
React.useEffect(onUpdate, [cart]);
const addToCart = (book) => {
const { key, image, bookName, by } = book;
let item = cart.find((item) => item.key === key);
if (item) {
item.amount++;
onUpdate();
} else {
setCart(
cart.concat({
amount: 1,
price: book.RegularPrice,
// add other cartItem attributes.
...book
})
);
}
};
I am trying to dispatch my action when the client clicks the "addToCart" btn. which will add a new product to the cart, but i get the following error: "TypeError: props.addToCart is not a function".
I am quite new to Redux and have learned the basics, but i cant seem to fix this problem.
code cartActions:
import * as actions from '../constants/cartConstants'
import store from '../storeRedux'
console.log(store.getState())
//go through all the items and add the item with the specific id
//with getState we can get whatever exists in the redux store
export const addToCart = (product,qty,count) =>(dispatch)=> {
let exists = false
const cartItems = store.getState().cart.cartItems.slice()
cartItems.forEach(item=> {
if(item.id === product.id){
exists = true
item.qty++
count++
qty++
}
})
if(!exists){
cartItems.push({...product, count : 1, qty: 1})
}
dispatch({
type: actions.ADD_TO_CART,
payload: {cartItems}
})
localStorage.setItem("cartItems", JSON.stringify(cartItems))
}
export const removeFromCart = (product)=>(dispatch) =>{
const cartItems = store.getState()
.cart.cartItems.slice()
.filter(
x => x.id !== product.id
)
dispatch({
type: actions.REMOVE_FROM_CART,
payload: {cartItems}
})
localStorage.setItem("cartItems",JSON.stringify(cartItems))
}
export const adjustQty = (product,qty)=> (dispatch)=> {
dispatch({
type: actions.ADJUST_QTY,
payload: {
product,
qty
}
})
}
export const reset =(cartItems,qty,count)=> (dispatch)=> {
dispatch({
type: actions.RESET,
payload: {
cartItems,
qty,
count
}
})
}
Code cartReducer:
import * as actions from '../constants/cartConstants'
const initialState = {
cartItems: JSON.parse(localStorage.getItem("cartItems")) || [] ,
count:0,
qty: 0,
amount: 0
}
const shopReducer = (
state = initialState,action)=> {
switch(action.type){
case actions.ADD_TO_CART:
return{
...state,
cartItems:action.payload.cartItems,
count: action.payload.count,
qty: action.payload.qty
}
case actions.REMOVE_FROM_CART:
return {
...state,
cartItems:action.payload.cartItems,
count: action.payload.count
}
case actions.RESET:
return{
...state,
cartItems: action.payload.cartItems =[],
qty: action.payload.qty =0,
count: action.payload.count =0,
amount: action.payload.amount =0
}
default:
return state;
}
}
export default shopReducer
Code productPage:
import React, { useEffect } from 'react'
import Nav from '../components/nav'
import '../styles/productdetails.css'
import {connect} from 'react-redux'
import { useDispatch, useSelector } from 'react-redux'
import { detailsProduct } from '../../actions/productActions'
import Review from '../components/review'
import { addToCart } from '../../actions/CartActions'
function ProductPage(props){
//manage quantity product
const productDetails = useSelector(state=> state.productDetails)
const{product,loading,error} = productDetails
const dispatch = useDispatch();
const cart = useSelector(state=> state.cart)
const {qty} = cart
useEffect(()=> {
dispatch(detailsProduct(props.match.params.id))
}, [])
return(
<div>
<Nav/>
<a className="product-a" href="/store">Back to products</a>
{loading? <div>loading...</div>: error? <div>{error}</div>:
(
<div className="productpage">
<div className="img" style={{background: `url(${product.img})`, backgroundSize: 'cover'}}></div>
<div className="description">
<h1>{product.name}</h1>
<p>{product.description}</p>
<span><small>€</small>{product.price}</span>
<div className="amount">
<p>Number:</p>
<label>
<button type="button" className="btnInc" onClick={()=> {}}>+</button>
<input type="number"step="1" min="1" value={qty} />
<button type='button' className="btnDec" onClick={()=> {}}>-</button>
</label>
<div>Size: {product.size}</div>
</div>
{product.qty > 0? <button type="submit" className="addBtn" onClick={()=> {props.addToCart(product)}}> Add to cart</button> : <div>Out of stock</div>}
</div>
</div>
)}
<Review/>
<div className="reviews">
<h3>username</h3>
<p>reviews : 3 out of 5</p>
<p>description of what he says</p>
</div>
</div>
)
}
export default connect(null,addToCart)(ProductPage)
You should do the following:
export default connect(null,{addToCart})(ProductPage)
According to the documentation the mapDispatchToProps parameter can either be a function or an object (or undefined).
If it is a function then it is expected to return an object and each property of that object is expected to be a function that can be called by your component to dispatch an action.
If it is an object then each property of that that object should be a function that returns an action (action creator). Connect will replace that function with a function that will pass arguments to the action creator and dispatches the resulting action.
So {addToCart} is shorthand for: {addToCart: addToCart} which is an object with an addTocart property that has a value of the action creator named addToCart.