0

So I have 2 buttons for adding an item into a cart. The item has 2 prices(1/2kg price and 1kg price). I want when the 1/2kg button is clicked, it adds the item with the 1/2kg price and when the 1kg button is clicked, it adds an item with the 1kg price. So far both buttons add the 1/2kg price because that is the only price I had until they decided to add 1kg of the item. Here is my code below:

PS: price is the 1/2kg price and price2 is the 1kg price.

const CollectionItem = ({ item, addItem }) => {
    const { name, price, price2, imageUrl } = item;

    return (
        <div className="collection-item">
            <div className="image" style={{ backgroundImage: `url(${imageUrl})` }} />
            <div className="collection-footer">
                <span className="name">{name}</span>
                <div className="prices">
                    <strong>
                        <span className="price">{price}</span>
                    </strong>
                    <strong>
                        <span className="price2">{price2}</span>
                    </strong>
                </div>
            </div>
            <div className="add-buttons">
                <CustomButton
                    className="custom-button"
                    onClick={() => addItem(item, 'price')}
                    inverted
                >
                    Add 1/2kg
                </CustomButton>
                <CustomButton
                    className="custom-button"
                    onClick={() => addItem(item, 'price2')}
                    inverted
                >
                    Add 1kg
                </CustomButton>
            </div>
        </div>
    );
};

const mapDispatchToProps = (dispatch) => ({
    addItem: (item) => dispatch(addItem(item)),
});

The addItem is an action creator I used to update my reducer which uses the function below from a cartUtils file I created to update:

export const addItemToCart = (cartItems, cartItemToAdd) => {
    const existingCartItem = cartItems.find(
        (cartItem) => cartItem.id === cartItemToAdd.id
    );

    if (existingCartItem) {
        return cartItems.map((cartItem) =>
            cartItem.id === cartItemToAdd.id
                ? { ...cartItem, quantity: cartItem.quantity + 1 }
                : cartItem
        );
    }

    return [...cartItems, { ...cartItemToAdd, quantity: 1 }];
};

my action is:

export const addItem = (item) => ({
    type: CartActionTypes.ADD_ITEMS,
    payload: item,
});

my reducer is:

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

I hope you good people can help me!

2
  • Your question is not clear. Your explanation is also confusing. Try to be more explicit about the issue you're facing and what steps you have tried to solve the problem so far. That way more people can jump into your question to help you out Commented Oct 6, 2020 at 18:49
  • @Alex Yepes Lemme restructure the question again. Check it out again. Commented Oct 6, 2020 at 21:14

1 Answer 1

1

I see two possible ways:

  • instead of addItem(), create two separated functions where for 0,5kg onClick action only item.price will be in use and second function for 1kg where you use item.price2

or

  • add second parameter to the addItem() function so it will be possible to differ which price you should use in particular action, like:
const addItem = (item, priceType) => {
  switch (priceType) {
    case 'price': 
       // 0,5kg 
    case 'price2': 
       // 1kg 
    default: //default case
  }
}

...
<div>
  <CustomButton
    ...
    onClick={() => addItem(item, 'price')}
  >
    Add 1/2kg
  </ CustomButton>
  <CustomButton
    ...
    onClick={() => addItem(item, 'price2')}
  >
    Add 1kg
  </ CustomButton>
</div>


REDUX

I suggest you to use method no.1 without anti pattern. I have also added method no.2 where I keep the original author way of thinking.

METHOD 1. WITHOUT ANTI PATTERN

  1. In React CollectionItem component use mapStateToProps so you'll get cartItems as a props from the STORE.
  2. Rewrite function triggered onClick event so it do all necessary changes with cartItems and then return updated cartItems list in action.
  3. Reducer just take updated argument and pass it down to the STORE, without any mutations.
const CollectionItem = ({ item, addItem, cartItems }) => {
  const { name, price, price2, imageUrl } = item;

  const addItemToList = (item, priceType) => {
    // addItemToCart() logic 
    // cartItems argument is available as a props from STORE
    addItem(updatedCartItems); //call action
  }
  return (
    ...
      
    <CustomButton
      ...
      onClick={() => addItemToList(item, 'price')}
    >
      Add 1/2kg
    </ CustomButton>
  )
}

const mapStateToProps = (state) => ({
  cartItems: state.cartItems
});
const mapDispatchToProps = (dispatch) => ({
    addItem: (updatedCartItems) => dispatch(addItem(updatedCartItems)),
});
export default connect(mapStateToProps, mapDispatchToProps)(CollectionItem);

Action creator

export const addItem = (updatedItems) => ({
    type: CartActionTypes.ADD_ITEMS,
    payload: updatedItems,
});

Reducer

case CartActionTypes.ADD_ITEMS:
  return {
    ...state,
    cartItems: action.payload,  // PURE REDUCER
  };

METHOD 2. ANTI PATTERN in reducer

As you updated addItem(item, 'price') so it receives two parameters, now it's time to update your action creator and reducer so it will be able to accept second parameter 'price' or 'price2'.

Action Creator - added priceType

export const addItem = (item, priceType) => ({
    type: CartActionTypes.ADD_ITEMS,
    payload: item,
    priceType
});

Reducer - pass down the priceType

CAUTION! Using addItemToCart() inside reducer is anti pattern. According to the redux documentation you should never mutate arguments inside reducer, it should stay pure.

case CartActionTypes.ADD_ITEMS:
  return {
    ...state,
    cartItems: addItemToCart(state.cartItems, action.payload, action.priceType),
  };

Then addItemToCart(cartItems, cartItemToAdd, priceType) can use switch(priceType) so it will differ price type.

Sign up to request clarification or add additional context in comments.

4 Comments

Hello @Aga, thanks for the response. I updated my question and added more details. Just check it and see what the solution could be. I used redux and addItem is an action creator. Kindly read the question again and see if you can help
Hey @BrianWambura, I have added redux part. Method no2 is more the way you did it. Eventually, I recommend you method no1 where you won't mutate redux argument.
Hey @Aga, are you able to kindly take zoom calls...I can schedule a short meeting so we could discuss this solution a bit more. I am having a few more issues.
@BrianWambura I am not sure If I am able to clarify all your concerns. What other question do you have?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.