JavaScript In React/Redux, how to calculate a Total price for a shopping cart react redux calculator,react redux calculator example,react redux calculator

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.

Answer:1

I think you need to change the way you structured the cart object stored in the cart. It should be something like this

cart: [
  {
    key: /* UNIQUE KEY TO IDENTIFY ITEM IN*/
    price: /* Number */
    quantity: /* number */
    total: /*price*quantity*/
  },
  {
    key: /* UNIQUE KEY TO IDENTIFY ITEM IN*/
    price: /* Number */
    quantity: /* number */
    total: /*price*quantity*/
  },
]

With the above cart structure you can update a single item, add any item or delete any item from the cart and you in the reducer basically you can iterate over the cart array and calculate the total price using the total key present in every object and then update total in the store.

I hope it helps. Thanks

Answer:2

It looks like you do it in a really wrong way. You should keep a list of cart items in the state. In this case you will be able to calculate cart total any time you need it, in any place, not necessary in reducer. The code should look like this

Action

export const addItem = (item/*{id, price}*/, quantity) => dispatch => {

  return dispatch({
    type: ADD_ITEM,
    item,
    quantity
  });
};

export const removeItem = (item/*{id, price}*/, quantity) => dispatch => {

  return dispatch({
    type: REMOVE_ITEM,
    item,
    quantity
  });
};

Reducer

const INITIAL_STATE = {
  items: {},
  total: 0
};

const cartReducer = (state = INITIAL_STATE, action) => {
  switch (action.type) {
    // Update single item price
    case Types.ADD_ITEM:
      {
          const items = Object.extend(state.items);
          const { item, quantity } = action;
          if (items[item.id]) {
            items[item.id].quantity += quantity;
          } else {
            items[item.id] = {price: item.price, quantity};
          }
          const total = Object.values(items)
            .reduce((result, cartItem) => result + cartItem.price*cartItem.quantity, 0);

          return {
            ...state,
            items,
            total
          };
      }

    case Types.REMOVE_ITEM:
      {
          const items = Object.extend(state.items);
          const { item, quantity } = action;
          if (items[item.id]) {
            items[item.id].quantity -= quantity;
          } else {
            items[item.id] = {price: item.price, quantity};
          }
          if (items[item.id] <= 0) {
            delete items[item.id];
          }
          const total = Object.values(items)
            .reduce((result, cartItem) => result + cartItem.price*cartItem.quantity, 0);

          return {
            ...state,
            items,
            total
          };
      }

    default:
      return state;
  }
};

The code is just to demonstrate a general idea, reducers code is a copy/paste and common parts can be extracted.

Answer:3

Here's my full code: $('input').keypress(function(e){ var code = e.keyCode || e.which, value = $(this).val() if (code==13 && value!=''){ $('.with-header').show() ...

Here's my full code: $('input').keypress(function(e){ var code = e.keyCode || e.which, value = $(this).val() if (code==13 && value!=''){ $('.with-header').show() ...

  1. remove autofocus html
  2. remove input autofocus
  3. remove autofocus input html

How can I show SweetAlert in this JavaScript code? function removeReg(del_reg) { if (confirm("Are you sure you want to delete? \n the reg name : " + del_reg)) { // Code goes here } } I just ...

How can I show SweetAlert in this JavaScript code? function removeReg(del_reg) { if (confirm("Are you sure you want to delete? \n the reg name : " + del_reg)) { // Code goes here } } I just ...

I'm trying to figure out how to set up a reducer for a property in my state tree that gets created from user events. My state tree looks like this: { session: { session object }, dashboard: { ...

I'm trying to figure out how to set up a reducer for a property in my state tree that gets created from user events. My state tree looks like this: { session: { session object }, dashboard: { ...

I'm trying to hide the "collapsible" button after it is being pressed. activities.html: <button class="collapsible"> <i class="fa fa-angle-down"></i></a></button> ...

I'm trying to hide the "collapsible" button after it is being pressed. activities.html: <button class="collapsible"> <i class="fa fa-angle-down"></i></a></button> ...

  1. hiding button after collapse is toggle in bootstrap