Reputation: 5073
I am having some trouble changing the colour of an item that has been added to a cart at an e-commerce project. I can make it so that when the item is clicked, the class changes and the item get a colour. As a side effect, all accompanying items get the colour as well.
The 'Service' component
const Service = (props) => {
const context = useContext(ThemeContext)
return (
<>
<li className={context.cartItems.some(item => item.type === "service") ? "inCart" : ""}
onClick={() => { context.cartItems.some(item => item) ?
context.removeFromCart(props) : context.addToCart(props)}} >
{props.name}
</li>
</>
)
}
The 'Options' component which renders the above component
const Options = () => {
const context = useContext(ThemeContext)
const serviceElements = servicesList.map(service =>
<Service key={service.id} id={service.id} name={service.name} type={service.type} /> )
return (
<div className={`Options-${context.theme}`}>
<ul>
{serviceElements}
</ul>
</div>
)
}
The add and remove from cart methods
function addToCart(newItem) {
cartItems.map(item => newItem.type === item.type && removeFromCart(item))
setCartItems(prevItems => [...prevItems, newItem])
}
function removeFromCart(itemToRemove) {
setCartItems(prevItems => prevItems.filter(item =>
`${item.id}-${item.type}` !== `${itemToRemove.id}-${itemToRemove.type}`))
}
Scss
.Options-light {
.inCart {
background-color: blue;
}
Upvotes: 2
Views: 253
Reputation: 5581
In your Service
component you are use context.cartItems.some
to determine if "inCart" should be used as the className
. This is the problem because some()
will be true
if any items are in the cart. That means every <li>
will either have no className
, or all with use "inCart".
What you really want is whether the current item is in the cart so I recommend the following changes:
const Service = (props) => {
const context = useContext(ThemeContext);
const inCart = context.cartItems.find(item => item.id === props.id);
return (
<>
<li className={inCart ? "inCart" : ""}
onClick={() => {
inCart ?
context.removeFromCart(props) :
context.addToCart(props)
}} > {props.name}
</li>
</>
)
}
This will only set <li className="inCart"
for list items that are in the cart.
Your onClick
also uses some()
incorrectly. The way it's written it will always be true
and always will call removeFromCart(props)
. I changed it to act as I expect you intended, which is to add it if the item isn't there, and remove if it is.
Upvotes: 2
Reputation: 1536
Looks like a couple things can change. First of all, your className
logic in Service
is going to add inCart
to all of your li
's because it's checking the whole context array independent of the current props. So try:
context.cartItems.find(item => item.id === props.id) ? "inCart" : ""
Also can clean up your content functions:
function addToCart(newItem) {
// Looks like you were trying to do a check to make sure that the item isn't
// already in the cart
const alreadyInCart = cartItems.some(item => item.id === newItem.id);
if (!alreadyInCart) {
setCartItems(prevItems => [...prevItems, newItem]);
}
}
function removeFromCart(itemToRemove) {
// I assume id is unique because you use it as a key for the Service component
setCartItems(prevItems => prevItems.filter(item => item.id !== itemToRemove.id));
}
I think you'd probably be better off with a toggleCartItem
though to make things cleaner and if your later "Cart" service only needs an id, then you can clean up further. Let me know if this works a little better:
const Options = () => {
const context = useContext(ThemeContext);
const serviceElements = servicesList.map(service =>
<Service
key={service.id}
id={service.id}
inCart={context.cartItems.some(id => service.id === id)}
/>
);
return (
<div className={`Options-${context.theme}`}>
<ul>
{serviceElements}
</ul>
</div>
)
}
const Service = (props) => {
const context = useContext(ThemeContext);
return (
<li
className={props.inCart ? "inCart" : ""}
onClick={() => toggleCartItem(props.id)}
>
{props.name}
</li>
)
}
function toggleCartItem(newItemId) {
const alreadyInCart = cartItems.some(id => id === newItemId);
if (alreadyInCart) {
setCartItems(prevIds => prevId.filter(id => id !== newItemId));
} else {
setCartItems(prevIds => [...prevIds, newItemId]);
}
}
Upvotes: 3
Reputation: 490
Your SCSS doesn't have a closing bracket? it should be:
.Options-light {
.inCart {
background-color: blue;
}
}
Upvotes: 0