I've searched for solutions on Google and SO but still cannot find an answer. They all stop at "Add item to cart" or "increase/decrease quantity" but never on calculating the Total, which is annoying!
In my app, there's a list of items where the user can enter the quantity if an item, which updates the its price. All I want to know is how do you sum up all the prices of all items in a cart into a Total price, with Redux and display it in my React app?
Also, if you can point me to any good shopping cart tutorial that actually goes beyond listing products in a cart, I'll be glad.
/**
* @description UPDATE SINGLE ITEM PRICE WHEN USER ENTER QUANTITY
*
* @param{number} price
* @param{number} quantity - enter by user from input
*/
export const updateItemPrice = (price, quantity) => dispatch => {
const result = price * quantity;
return dispatch({
type: UPDATE_ITEM_PRICE,
subtotal: result
});
};
const INITIAL_STATE = {
total: 0,
subtotal: 0
};
const productsReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
// Update single item price
case Types.UPDATE_ITEM_PRICE:
return {
...state,
subtotal: action.subtotal,
total: // --> Here is where I'm stuck!!
};
default:
return state;
}
};
EDIT: More complete example of state/actions/reducers.
Do you actually need to store the totals in redux? Generally you want to keep the minimal state in redux, and calculate any derived data that you can in a selector. Subtotals and totals definitely fall into this category (unless you have a really unusual set your own price set up or something), so instead of storing them in the store, you can calculate them as needed, for example as part of your mapStateToProps
function (assuming you're using react-redux
).
Here's an example of what your state could look like. It includes two main slices, one for the catalog of items, and a second one specifically for the card.
{
itemDetails: {
item01: { name: 'Item One', price: 9.95 },
item02: { name: 'Item Two', price: 10 },
item03: { name: 'Item not appearing in this example', price: 50 },
},
cart: {
item01: 1,
item02: 2,
},
}
Looking at the cart slice, all the reducer for that needs to do is manage the quantity in the cart, which you can do with basic actions. The actions and reducer may look something like (none of this is tested, is just to provide a feel for how this may look):
// Cart actions
const incrementQuantityInCart = (itemId) => ({
type: 'incrementQuantityInCart',
itemId,
})
const decrementQuantityInCart = (itemId) => ({
type: 'decrementQuantityInCart',
itemId,
})
const removeItemFromCart = (itemId) => ({
type: 'removeItemFromCart',
itemId,
})
// Cart reducer, would be combined with a separate reducer using redux's `combineReducers`
const cart = (state = {}, action) => {
switch (action.type) {
case 'incrementQuantityInCart':
const currentQuantity = state[action.itemId] || 0
return {
...state,
[action.itemId]: currentQuantity + 1,
}
case 'decrementQuantityInCart':
const currentQuantity = state[action.itemId] || 0
return {
...state,
[action.itemId]: Math.max(currentQuantity - 1, 0),
}
case 'removeItemFromCart':
return {
...state,
[action.itemId]: 0,
}
default:return state
}
}
You could then have a selector such as:
function getCartContents(state) {
const itemsInCart = Object.keys(state.cart)
.filter(itemId => state.cart[itemId] > 0)
.map(itemId => {
const quantity = state.cart[itemId]
const itemDetail = state.itemDetails[itemId]
return {
name: itemDetail.name,
price: itemDetail.price,
quantity,
subtotal: itemDetail.price * quantity,
}
})
const total = itemsInCart.reduce((total, item) => total + item.subtotal)
return { itemsInCart, total }
}
// example output based on the above state
// {
// itemsInCart: [
// {
// name: 'Item One',
// price: 9.95,
// quantity: 1,
// subtotal: 9.95,
// },
// {
// name: 'Item Two',
// price: 10,
// quantity: 2,
// subtotal: 20,
// },
// ],
// total: 29.95,
// }
You can then use this function either in or as your mapStateToProps
for whatever component you want and it will have access to this data in it's props, so you can use as required.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With