【问题标题】:React-Redux Shopping Cart - Decrease/Increase Item QuantityReact-Redux 购物车 - 减少/增加商品数量
【发布时间】:2018-04-02 00:58:51
【问题描述】:

我正在做一个关于 React-Redux 购物车的项目。我目前正在尝试构建功能以允许用户更新添加到购物车的商品数量。我已经能够让“从购物车中删除”工作。我一直在寻找不同的方法,但似乎所有购物车教程都停在“添加到购物车”!所以我一直试图自己解决它,但在网上找到的例子很少。谁能指出我正确的方向?

这是最初发布在 Github 上的购物车教程: https://github.com/reactjs/redux/tree/master/examples/shopping-cart

这是我一直试图弄清楚的:

ProductItem.js

const ProductItem = ({product, onAddToCartClicked, onRemoveFromCartClicked, onIncreaseQuanityClicked, onDecreaseQuantityClicked }) => (
  <div style={{ marginBottom: 20, marginLeft: 20}}>
  <Card>
    <CardBody>
    <Product
      title={product.title}
      price={product.price}
      inventory={product.inventory} />
    <Button color="primary"
      onClick={onAddToCartClicked}
      disabled={product.inventory > 0 ? '' : 'disabled'}>

      {product.inventory > 0 ? 'Add to cart' : 'Sold Out'}
    </Button>
          <Button color="success"
            onClick={onIncreaseQuanityClicked}
            disabled={product.inventory > 0 ? '' : 'disabled'}> +
          </Button>
          <Button color="danger"
            onclick={onDecreaseQuantityClicked} 
            disabled={product.inventory > 0 ? '' : 'disabled'}> - 
            </Button>

          <Button onClick={onRemoveFromCartClicked}>Remove</Button>
    </CardBody>
    </Card>
  </div>
)

ProductsContainer.js

const ProductsContainer = ({ products, addToCart }) => (
  <ProductsList title="Products">
    {products.map(product =>
      <ProductItem
        key={product.id}
        product={product}
        onAddToCartClicked={() => addToCart(product.id)}
        onIncreaseQuantityClicked={() => increaseQuantity(product.id)} 
        onDecreaseQuantityClicked={() => decreaseQuantity(product.id)} />
    )}
  </ProductsList>
)

ProductsContainer.propTypes = {
  products: PropTypes.arrayOf(PropTypes.shape({
    id: PropTypes.number.isRequired,
    title: PropTypes.string.isRequired,
    price: PropTypes.number.isRequired,
    inventory: PropTypes.number.isRequired
  })).isRequired,
  addToCart: PropTypes.func.isRequired,
  increaseQuantity: PropTypes.func.isRequired
}

const mapStateToProps = state => ({
  products: getVisibleProducts(state.products)
})

export default connect(
  mapStateToProps,
  { addToCart, increaseQuantity, decreaseQuantity }
)(ProductsContainer)

reducer/products.js

const products = (state, action) => {
  switch (action.type) {
    case ADD_TO_CART:
      return {
        ...state,
        inventory: state.inventory - 1
      }
    case REMOVE_FROM_CART:
      return {
        ...state,
        inventory: state.inventory + 1
      }
    case INCREASE_QUANTITY:
      return {
        ...state,
        //NOT SURE WHAT ELSE TO PUT HERE
      }
    case DECREASE_QUANTITY:
      return {
        ...state,
        NOT SURE WHAT ELSE TO PUT HERE EITHER
      }
    default:
      return state
  }
}

谁能指出我正确的道路?如果我什至走在正确的道路上,或者建议任何可以提供帮助的教程或网站?

【问题讨论】:

  • 你的状态树是什么样子的?
  • 嘿@JosanIracheta,你必须原谅我,但我还是 React/Redux 的新手,我不知道如何获取状态树
  • 状态树是您将状态构造为对象的方式。

标签: javascript reactjs redux react-redux cart


【解决方案1】:

首先,我认为您应该在您的状态中包含quantity,并将数量的逻辑与inventory 分开。以下是您的状态树的样子:

{
  cart: [
          {id: 1, quantity: 3},
          {id: 3, quantity: 2}
         ],
  products: [
          {id: 1, inventory: 10, ...},
          {id: 2, inventory: 10, ...},
          {id: 3, inventory: 10, ...}
            ]
}

购物车存储添加到购物车的产品,产品包含所有可用产品。

考虑到这个状态树,我们可以使用以下动作创建者:

function quantityUp(id, val){
  return {type: 'QTY_UP', id, up: val}
}
function quantityDown(id, val){
  return {type: 'QTY_DOWN', id, down: val}
}

现在,我们可以创建我们的 reducer。既然我们把数量和库存分开了,我们也应该把reducer分开来体现这个逻辑。

const cart = (state, action) => {
  switch(action.type){
    case 'QTY_UP':
      return Object.assign([], state.map(item => {
        if(item.id === action.id){
          item.quantity += action.up;
        }
        return item;
      ));
    case 'QTY_DOWN':
      return Object.assign([], state.map(item => {
        if(item.id === action.id){
          item.quantity -= action.down;
        }
        return item;
      ));
     default:
       return state;
   }
};

以下操作也应该是您的购物车减速器的一部分:ADD_TO_CART, REMOVE_FROM_CART

如果需要,products reducer 应该自己修改产品。一种情况是在购买物品后修改物品的库存。

让我们先创建动作创建者:

//cart will be an array
function purchase(cart){
  return {type: 'PURCHASE', cart}
}

现在我们可以创建reducer了:

const products = (state, action) => {
  switch(action.type){
    case 'PURCHASE':
      const ids = action.cart.map(item => item.id);
      return Object.assign([], state.map(item => {
        if(ids.includes(item.id)){
          item.inventory -= action.cart.filter(p => p.id === item.id)[0].quantity;
        }
        return item;
      }));
    case default:
      return state;
    }
  };

现在我们可以将产品添加到您的购物车,编辑购物车中每种产品的数量,并在购买产品时更新您所在州的每种产品的库存。

【讨论】:

  • 感谢您写下这篇文章!不幸的是,我正在使用一个预先存在的代码库,所以我不能比现在更多地改变它。但是我以后可能会尝试制作自己的购物车应用程序,所以我会尝试实现这个设计。
【解决方案2】:

确保在你的 reducer 之前有一个初始状态设置,例如:

const initialState = {
  inventory: 0,
  quantity: 0
}

然后你将你的 reducer 链接到你刚刚声明的状态:

const products = (state = initialState, action) => {

如果您希望您的操作增加您在状态下的数量,您可以像处理库存一样继续:

quantity: state.quantity + 1

提醒一下,状态首先是通过一个或多个 reducer 启动的,然后你在 redux 中创建一个 store,例如使用

const store = createStore(yourReducer)

或

const store = createStore(combineReducers(allYourReducers))

您的商店将拥有您的应用的全局状态,该状态由您所有 reducer 的初始状态的总和组成。

然后您可以通过调度您的操作来访问和使用状态

store.dispatch(yourAction)

如果您的应用程序中的所有内容都连接良好,您应该会看到您想要的状态更新。

您可以查看 Andrew Mead 的课程:https://www.udemy.com/react-2nd-edition/learn/v4/overview

【讨论】:

    【解决方案3】:

    鉴于您当前的代码,addToCart 和 increaseQuantity 是同一个东西。

    您可以:

    1) 在您的容器中重用addToCart 函数

    <ProductItem
        key={product.id}
        product={product}
        onAddToCartClicked={() => addToCart(product.id)}
        onIncreaseQuantityClicked={() => addToCart(product.id)} 
        onDecreaseQuantityClicked={() => decreaseQuantity(product.id)} />
    

    2) 在你的 reducer 中实现相同的逻辑

    case INCREASE_QUANTITY:
      return {
        ...state,
        inventory: state.inventory - 1
      }
    

    【讨论】:

      【解决方案4】:

      当我们处理购物车时,我们的购物车状态中应该有 cartItems 数组,每次我们点击“添加到购物车”按钮时,该商品将被推送到该数组,我们将“映射”该数组到我们要渲染购物车项目的组件。

      const INITIAL_STATE = {
        //you could have more properties but i focus on cartItems
        cartItems: []
      };
      

      要将商品添加到购物车,我们在编写代码时应该小心。因为第一次将商品添加到购物车很容易,但是如果我们多次将相同的商品添加到购物车会怎样。所以我们需要对 cartItems 数组中的项目进行分组。为此,我们需要编写一个实用函数。

      //cart.utils.js
      
      export const addItemToCart = (cartItems, cartItemToAdd) => {
        //find(condition) finds the first item in the array based on the condition.
        const existingCartItem = cartItems.find(item => item.id === cartItemToAdd.id);
        if (existingCartItem) {
          //in order for change detection to trigger we have to rerender
          //otherwise our quantity property will not be updated
          //map will return a new array 
          //we need to return new versions of our state so that our component know to re render
          //here we update the quantity property
          return cartItems.map(item =>
            item.id === cartItemToAdd.id
              ? { ...cartItemToAdd, quantity: item.quantity + 1 }
              : item
          );
        }
        //when you first time add a new item, sine exixtingCartItem will be falsy, it will pass the first if block and will come here
        //quantity property gets attached the first time around since this if block wont run when it is a new item.
       //in the beginning cartItems array is empty. every time you add a new item to this array, it will add "quantity:1" to this item object.  
        return [...cartItems, { ...cartItemToAdd, quantity: 1 }];
      };
      

      在你的 reducer 文件中

      从“./cart.utils”导入 { addItemToCart };

      case INCREASE_QUANTITY:
           return {
              ...state,
      
              cartItems: addItemToCart(state.cartItems, action.payload)
            };
      

      为了从购物车中移除商品,我们需要编写另一个实用函数。

        cart.utils.js
      
      export const removeItemFromCart = (cartItems, cartItemToRemove) => {
        //check if item is already in the cartItems
        const existingCartItem = cartItems.find(
          item => item.id === cartItemToRemove.id
        );
        //if there is only 1, upon clicking, we should remove the item from the array
        if (existingCartItem.quantity === 1) {
          return cartItems.filter(item => item.id !== cartItemToRemove.id);
        }
      
        return cartItems.map(item =>
          item.id === cartItemToRemove.id
            ? { ...item, quantity: item.quantity - 1 }
            : item
        );
      
      };
      

      在 reducer/products.js 中

      import { addItemToCart, removeItemFromCart } from "./cart.utils";
      
      case DECREASE_QUANTITY:
        return {
          ...state,
          cartItems: removeItemFromCart(state.cartItems, action.payload)
        }
      

      【讨论】:

        猜你喜欢
        • 2020-08-01
        • 2017-11-21
        • 2020-06-17
        • 2015-06-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-28
        相关资源
        最近更新 更多