在React / Redux中,如何计算购物车的总价

时间:2018-08-01 06:31:00

标签: javascript reactjs redux react-redux

我已经在Google和SO上搜索了解决方案,但仍然找不到答案。它们都停留在“将商品添加到购物车”或“增加/减少数量”处,但从不计算总数,这很烦人!

在我的应用中,有一个商品列表,用户可以在其中输入商品的数量,这会更新其价格。 我只想知道如何使用Redux将购物车中所有项目的所有价格汇总为总计价格,并将其显示在我的React应用中?

此外,如果您可以将我引到实际上不在列出购物车中产品的任何购物车教程中,我将很高兴。

动作:

/**
 * @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;
  }
};

5 个答案:

答案 0 :(得分:5)

编辑:状态/动作/还原器的更完整示例。

您是否真的需要将总计存储在redux中?通常,您希望将最小状态保留在redux中,并计算可以在选择器中使用的所有派生数据。小计和总计肯定属于此类别(除非您设置了非常不寻常的价格设置或其他东西),因此您可以根据需要计算它们,而不是将其存储在商店中,例如作为{{1 }}函数(假设您使用的是mapStateToProps)。

这是您的状态看起来的一个例子。它包括两个主要部分,一个用于物品目录,另一个用于卡片。

react-redux

查看购物车切片,所需做的所有减速器都是管理购物车中的数量,您可以使用基本操作来进行此操作。动作和减速器可能看起来像(没有经过测试,只是为了提供这种感觉)

{
  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,
  },
}

然后可以有一个选择器,例如:

// 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
  }
}

然后,您可以在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, // } 中使用该函数或将其用作所需的任何组件,并且它可以访问其props中的此数据,因此您可以根据需要使用。

答案 1 :(得分:0)

我认为您需要更改存储在购物车中的购物车对象的结构方式。应该是这样的

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*/
  },
]

使用上面的购物车结构,您可以更新一个商品,添加任何商品或从购物车中删除任何商品,而在reducer中,您基本上可以遍历购物车数组,并使用每个商品中存在的总键来计算总价对象,然后在商店中更新总计。

我希望它会有所帮助。谢谢

答案 2 :(得分:0)

您似乎以错误的方式进行了操作。您应该保持状态中的购物车清单。在这种情况下,您将能够在任何时候,任何地方,在reducer中不需要的任何时候计算购物车总数。代码应该像这样

动作

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
  });
};

减速器

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;
  }
};

该代码仅是为了展示一个总体思路,reducers代码是一种复制/粘贴操作,并且可以提取常用部分。

答案 3 :(得分:0)

我不使用Redux,但计算含税的总价格是纯JavaScript。首先,您应该在items对象中将“ total”属性设置为0。 item.total为0。

state = {
    products: [],
    cart: [], // you are populating cart in "addToCart"  method
    cartSubTotal: 0,
    cartTax: 0,
    cartTotal: 0
  };
addTotals = () => {
    let subTotal = 0;
    this.state.cart.map(item => (subTotal += item.total)); 
    const tempTax = subTotal * 0.1;  //10% tax 
    const tax = parseFloat(tempTax.toFixed(2)); //limiting tax to 2 decimal numbers
    const total = subTotal + tax;
    this.setState(() => {
      return {
        cartSubTotal: subTotal,
        cartTax: tax,
        cartTotal: total
      };
    });
  };

设置状态时,还应该在“ addToCard”方法中调用“ addTotals”方法。我将展示如何在“ this.setState”方法中实现它。

addToCart方法

this.setState(
      () => {
        return {
          products: [tempProducts],
          cart: [...this.state.cart, product],
          detailProduct: { ...product }
        };
      },
      () => this.addTotals()
    );

答案 4 :(得分:0)

您可以使用本机javascript reduce()。您将使用产品对象的数量和价格属性将cartItem减少为总价。

如果您在购物车上工作,则应该具有具有“ cartItems”数组属性的购物车减速器和购物车状态。另一件事是每个产品对象都应具有数量和价格对象。

const INITIAL_STATE = {
  //you might have more properties
  cartItems: []
};

因此要在您的组件(通常是结帐组件)中显示总价,请输入以下内容:

const mapStateToProps = state => {
  total: state.cart.cartItems.reduce(
    //reduce go through the array and cartItem is the each item in the array
    (accumulatedTotal, cartItem) =>
      accumulatedTotal + cartItem.price * cartItem.quantity,
    0 //0 is the start point of accumulatedTotal
  );
};

然后您可以将“总计”作为属性传递给组件

这是结帐页面的示例

const CheckoutPage = ({ cartItems, total }) => (
  <div className="checkout-page">
    <div className="checkout-header">
      <div className="header-block">
        <span>Product</span>
      </div>
      <div className="header-block">
        <span>Description</span>
      </div>
      <div className="header-block">
        <span>Quantity</span>
      </div>
      <div className="header-block">
        <span>Quantity</span>
      </div>
      <div className="header-block">
        <span>Remove</span>
      </div>
    </div>
    {cartItems.map(cartItem => (
      <CheckoutItem key={cartItem.id} cartItem={cartItem} />
    ))}
    <div className="total">
      <span>TOTAL:${total}</span>
    </div>
  </div>
);