I'm trying to implement a shopping cart using redux. here is my cart-reducer:
export const cartReducer = (
state = { cartItems: JSON.parse(localStorage.getItem("cartItems") || "[]")},
action) => {
switch (action.type) {
case ADD_TO_CART:
return { ...state,cartItems: action.payload };
}}
Here is the component where I want to show the updated state value accessing it using props in connect and update the cartItems.length after every state update
class Cartsidebar extends Component {
constructor(props) {
super(props);
this.state = {
grandTotal: '',toggle:true
}
}
handleHide(){
this.setState({ toggle: !this.state.toggle})
}
render() {
const {cartItems}=this.props;
console.log(cartItems);
return (
<div>
{cartItems.length}
</div>
)
}
}
export default connect(
(state) => ({
cartItems: state.cart.cartItems,
}),
{ incrementToCart, decreaseToCart, removeFromCart }
)(Cartsidebar);
States are updating fine and state-difference is also showing in redux-dev-tools on every update of redux state but it is not reflecting in cart component.what am i doing wrong here?Thanks in advance.
EDIT: this is function that execute on add to cart button onclick event:
handleAddToCart=(p)=>{
const cartItems = store.getState().cart.cartItems;
let alreadyExists = false;
cartItems.forEach((x) => {
if (x.discountPer === p.discountPer) {
alreadyExists = true;
}
});
if (!alreadyExists) {
cartItems.push({ ...p });
}
store.dispatch(addToCart(cartItems));
}
And addToCart action creator looks like this:
export const addToCart = (cartItem) => {
return({
type: ADD_TO_CART,
payload: cartItem,
});
};
CodePudding user response:
What you are doing in handleAddToCart
is a big no no, and goes against the pattern that Redux tries to enforce. I made some changes to your logic to make it easier, and updated the reducer. In theory, if you make these changes, it should work.
handleAddToCart:
handleAddToCart = (p) => {
const cartItems = store.getState().cart.cartItems;
for (const item of cartItems) {
if (item.discountPer === p.discountPer) {
return;
}
}
store.dispatch(addToCart({ ...p }));
};
reducer:
export const cartReducer = (
state = { cartItems: JSON.parse(localStorage.getItem("cartItems") || "[]") },
action
) => {
switch (action.type) {
case ADD_TO_CART:
return { ...state, cartItems: [...state.cartItems, action.payload] };
}
};
CodePudding user response:
Issues
- You are mutating the state object. You are accessing a reference to the cart array in state and directly pushing into it.
- You aren't leveraging the power of Redux and reducers properly.
code
handleAddToCart = (p) => {
const cartItems = store.getState().cart.cartItems; // cartItems is reference to state
let alreadyExists = false;
cartItems.forEach((x) => {
if (x.discountPer === p.discountPer) {
alreadyExists = true;
}
});
if (!alreadyExists) {
cartItems.push({ ...p }); // mutation!!
}
store.dispatch(addToCart(cartItems));
}
Solution
Pass the item you want to add to the cart in the action and move all the logic to update the cart into your reducer.
UI
handleAddToCart = (p) => {
this.props.addToCart(p);
}
...
export default connect(
(state) => ({
cartItems: state.cart.cartItems,
}),
{ addToCart, incrementToCart, decreaseToCart, removeFromCart }
)(Cartsidebar);
reducer
case ADD_TO_CART:
const { payload } = action;
const found = state.cartItems.find(item => item.discountPer === payload.discountPer);
if (found) {
return state;
}
return {
...state,
cartItems: state.cartItems.concat(payload),
};