Home > Blockchain >  Add quantity in eshop in react-redux
Add quantity in eshop in react-redux

Time:10-27

I built an eshop and struggle find the syntax in order to multiply quantity of a product in the "Cart". I have managed to "add to cart" the products however does not update the quantity of the product added. It adds the product everytime I click "addToCart". I am using redux so I know that I should write additional code in the reducer file. My reducer file code is the following:

import { ADD_TO_CART } from './constants'
import { REMOVE_FROM_CART } from './constants'


const initialState = {
    cart: [],
  }
const ShoppinReducer = (state = initialState, action) => {
  switch (action.type) {
    case ADD_TO_CART:
      const newCart = [...state.cart]
      
      newCart.push(action.payload)
      return {
        ...state,
        cart: newCart
      }

    case REMOVE_FROM_CART:
      const cart = [...state.cart]
      const updatedCart = cart.filter(item => item.id !== action.payload.id)
      return {
        ...state,
        cart: updatedCart
      }
    
    default:
    return state
  }
}
  export default ShoppinReducer

and my "Cart" component is the following:

import React, { Component } from "react"
import { Card, CardBody, CardHeader, CardTitle, Row, Col } from "reactstrap"
import PanelHeader from "components/PanelHeader/PanelHeader.js"
import { connect } from "react-redux";
import { removeCart} from "../redux/actions";


class Cart extends Component {
   removeFromCart = (product) => {
    const cartProducts = this.props.cart 
    const updatedCartProducts = cartProducts.filter(item => item.id !== product.id);
  
  }

  render () {
    const cartProducts = this.props.cart
    return (
      <>
        <PanelHeader size="sm" />
        <div className="content">
          <Row>
            <Col xs={12}>
              <Card>
                <CardHeader>
                  <CardTitle tag="h4">Products List</CardTitle>
                </CardHeader>
                <CardBody>
                <table class="table table-striped table-hover">
                  <thead>
                    <tr>
                      <th scope="col"><strong>#</strong></th>
                      <th scope="col"><strong>Name</strong></th>
                      <th scope="col"><strong>Code Item</strong></th>
                      <th scope="col"><strong>Quantity</strong></th>
                      <th scope="col"><strong>Price Total</strong></th>
                    </tr>
                  </thead>
                    <tbody>
                      {cartProducts.length > 0 && cartProducts.map((cartProduct, index) => (             
                      <tr key={cartProduct.id}>
                    <th scope="row">{index  1}</th>
                    <td>{cartProduct.title}</td>
                    <td>{cartProduct.code}</td>
                    <td>{cartProduct.quantity}</td>
                    <td>{cartProduct.price}</td>
                    <td><button onClick ={() => this.props.removeCart(cartProduct)} className="btn btn-danger cart-button px-4">Remove</button></td>
                      </tr>))}
                    </tbody>
                </table>
              </CardBody>
            </Card>
          </Col>
        </Row>
      </div>
    </>
    )
  }
}


const mapStateToProps = (state)=> {
  return {
      cart: state.cart
       }
  }

const mapDispatchToProps = (dispatch) => { 
      return {
        removeCart: (product) => {dispatch(removeCart(product))}
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Cart);

Bare in mind that "addToCart" button lives in other component however, I posted "Cart" component in order to display how the structure of the "Cart" is! Thank you in advance!

CodePudding user response:

I hope I understand your question correctly. I would assume your cart state is something looking like this

cart: [
  {itemId: '1', quantity: 1, price: 100, ...},
  {itemId: '2', quantity: 1, price: 200, ...}
]
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe> The problem is your existing addToCart action will only push an item to the end of array, without checking if this was an item already exists in the cart. Eventually the cart end up like

cart: [
  {itemId: '1', quantity: 1, price: 100, ...},
  {itemId: '2', quantity: 1, price: 200, ...},
  {itemId: '1', quantity: 1, price: 100, ...}
]
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

So you need to check if you're adding a new item or an existing item, if it is an existing item, you need to update the existing item quantity rather than pushing it to the array.

CodePudding user response:

You can just check if the item is already in the list when the ADD_TO_CART action is executed. It could be something like

const alreadyInList = state.cart.find(item => item.id === action.payload.id);
let newCart;
if (alreadyInList) { // increments quantity if item already in list
  newCart = state.cart.map(item => {
    if (item.id === action.payload.id) {
      return {..item, quantity: item.quantity   1};
    } else {
      return item;
    }
  });
} else { // adds item to the end if it's not already in the list
  newCart = [...state.cart, payload.action];
}
return {..state, cart: newCart};

However, a JavaScript Array is not an adequate data structure for this kind of operation. It would be better if you used an object whose keys are de product IDs, and the values are the products themselves. Then you'd be able to check if an item is already present and update it more efficiently.

On a side note:

You check the length of cartProducts before mapping through the list

    {cartProducts.length > 0 && cartProducts.map((cartProduct, index) => (...

You don't need to do that, just cartProducts.map((cartProduct, index) => (... will suffice.

Also, you could take a look at react hooks and maybe redux toolkit. Both of these help to reduce boilerplate.

CodePudding user response:

As @spiritWalker says you have to implement that logic in the reducer. I give you an example:

    case ADD_TO_CART:
      const newCart = [...state.cart]
      
      const index = newCart.findIndex((item) => item.id == action.payload.id)
      // If index is -1, no similar items were found so we 
      // add the item to the cart.
      // Otherwise, the index of the item is returned and we use
      // it to update the quantity property of the existing item.
      if (index == -1) {
        newCart.push(action.payload)
      } else {
        newCart[index].quantity  
      }
      
      return {
        ...state,
        cart: newCart
      }
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

Also, I recommend you to give a try to the Redux Toolkit Package, which abstracts a lot of logic and boilerplate from Redux and can definitely make your code leaner and easier.

Happy coding!

CodePudding user response:

Before pushing any item to your cart, you need to check whether the items already exist in the cart or not, if it exists you should update the quantity instead. You can rewrite the ADD_TO_CART case in your reducer to something like this:

const initialState = {
    cart: [],
  }
const ShoppinReducer = (state = initialState, action) => {
  switch (action.type) {
    case ADD_TO_CART:
      let newCart = [...state.cart]
      let itemIndex = state.cart.findIndex(obj=>obj.id===action.payload.id)  
      let currItem = state.cart[itemIndex]

      if(currItem){
            currItem.quantity = parseInt(currItem.quantity)   1
            state.cart[itemIndex] = currItem
            newCart = [...state.cart]
        }
        else{
            
            newCart = newCart.concat(action.payload)
        }
        
        return {
          cart: newCart
        }

    case REMOVE_FROM_CART:
      const cart = [...state.cart]
      const updatedCart = cart.filter(item => item.id !== action.payload.id)
      return {
        ...state,
        cart: updatedCart
      }
    
    default:
    return state
  }
}
  • Related