So basically, I'm fetching products from an API and I added a new key value isAdded
to the object inside the products array, I used foreach
loop to add that key and its succesfully added, now when I hit Add to cart
button I'm adding the product item to my <Cart />
which is also working fine but the only thing I wanna do is when that specific product to added to the Cart I wanna change the text and style of the button, which means I wanna toggle the isAdded
value. But the isAdded
seems undefined when I click the button, rest data is going fine but not my key.
BookProduct.js
import BookItems from "./BookItems";
import classes from "./book-item.module.css";
import { useEffect } from "react";
export default function BookProducts({ booksData }) {
useEffect(() => {
booksData.forEach((element) => {
element.isAdded = false;
});
}, []);
console.log(booksData); //isAdded key perfectly added in the object
return (
<div className="container">
{booksData.map((el) => {
return (
<BookItems
id={el.isbn13}
key={el.isbn13}
imageUrl={el.image}
price={Number(el.price.slice(1))}
title={el.title}
subtitle={el.subtitle}
link={el.link}
isAdded={el.isAdded}
/>
);
})}
</div>
);
}
BookItems.js
import { useDispatch, useSelector } from "react-redux";
import Image from "next/image";
import Link from "next/link";
import classes from "./book-item.module.css";
import { cartStoreAction } from "../../store/cart-items";
export default function BookItems(props) {
const { id, title, price, imageUrl, isAdded } = props;
const dispatch = useDispatch();
const addToCartHandler = () => {
dispatch(
cartStoreAction.addProduct({
id,
title,
price,
imageUrl,
isAdded
})
);
console.log(isAdded); //isAdded is undefined
};
return (
<div className={classes.product__item}>
<Link href={`/products/${id}`} className={classes.image_box}>
<Image src={imageUrl} alt={title} className={classes.image} fill />
</Link>
<div className={classes.desc_box}>
<Link href={`/products/${id}`}>
<h3>{title}</h3>
</Link>
<div className={classes.bottom}>
<p>${price}</p>
<button onClick={addToCartHandler}>
{isAdded ? 'Added' : 'Add to cart'}
</button>
</div>
</div>
</div>
);
}
cart-items.js
const cartItemSlice = createSlice({
name: "cart",
initialState: {
items: [],
totalQuantity: 0,
totalAmount: 0,
},
reducers: {
addProduct(state, action) {
const newCartItems = action.payload; //items from dispatch object
const existingItem = state.items.find((el) => el.id === newCartItems.id);
state.totalQuantity ;
if (!existingItem) {
state.items.push({
id: newCartItems.id,
quantity: 1,
title: newCartItems.title,
price: newCartItems.price,
image: newCartItems.imageUrl,
totalPrice: newCartItems.price,
isAdded: !newCartItems.isAdded,
});
!newCartItems.isAdded;
} else {
existingItem.quantity ;
existingItem.totalPrice = existingItem.totalPrice newCartItems.price;
}
state.totalAmount = state.items.reduce(
(acc, index) => acc Number(index.price) * Number(index.quantity),
0
);
},
},
});
CodePudding user response:
It seems you are mutating the wrong object. Don't mutate passed props. I'd suggest skipping setting any isAdded
property in the addProduct
reducer function and selecting the state in the BookItems
component to know if an item has been added to the cart already by the item id.
cartItemSlice
const cartItemSlice = createSlice({
name: "cart",
initialState: {
items: [],
totalQuantity: 0,
totalAmount: 0,
},
reducers: {
addProduct(state, action) {
const { payload } = action; // items from dispatch object
const existingItem = state.items.find((el) => el.id === payload.id);
if (!existingItem) {
state.items.push({
quantity: 1,
...payload,
totalPrice: payload.price,
});
} else {
existingItem.quantity ;
existingItem.totalPrice = newCartItems.price;
isAddedToCart();
}
state.totalQuantity ;
state.totalAmount = state.items.reduce(
(acc, index) => acc Number(index.price) * Number(index.quantity),
0
);
},
removeProduct(state, action) {
const removingItemId = action.payload;
const alreadyAddedItem = state.items.find(
(item) => item.id === removingItemId
);
if (alreadyAddedItem.quantity === 1) {
state.items = state.items.filter((item) => item.id !== removingItemId);
} else {
alreadyAddedItem.quantity--;
alreadyAddedItem.totalPrice -= alreadyAddedItem.price;
}
state.totalQuantity--;
state.totalAmount = state.items.reduce(
(acc, index) => acc Number(index.price) * Number(index.quantity),
0
);
},
},
});
BookProducts
export default function BookProducts({ booksData }) {
return (
<div className="container">
{booksData.map((el) => (
<BookItems
id={el.isbn13}
key={el.isbn13}
image={el.image}
price={Number(el.price.slice(1))}
title={el.title}
/>
))}
</div>
);
}
BookItems
Select the cart items from the store and check if the current BookItems
id
prop is a match to an element in the cart items array.
import { useDispatch, useSelector } from "react-redux";
...
export default function BookItems({ id, title, price, image }) {
const dispatch = useDispatch();
const cartItems = useSelector(state => state.cart.items);
const isAdded = cartItems.some(item => item.id === id); // <-- compute here
const addToCartHandler = () => {
dispatch(cartStoreAction.addProduct({
id,
title,
price,
image,
}));
};
return (
<div className={classes.product__item}>
<Link href={`/products/${id}`} className={classes.image_box}>
<Image src={image} alt={title} className={classes.image} fill />
</Link>
<div className={classes.desc_box}>
<Link href={`/products/${id}`}>
<h3>{title}</h3>
</Link>
<div className={classes.bottom}>
<p>${price}</p>
<button onClick={addToCartHandler}>
{isAdded ? 'Added' : 'Add to cart'}
</button>
</div>
</div>
</div>
);
}