3

I want to remove object from my list by clicking on delete icon, but with my logic either everything is deleted from list or nothing, I am not sure how to do it without provided ID to object, I don't have anything unique and I am kinda lost. Component that renders as many Food as there is in useState:

 {cartFood.map((food) => {
    return (
      <CartFood
         key={Math.random()}
         foodName={food.foodName}
         foodPrice={food.foodPrice}
         numberOfPortions={food.numberOfPortions}
         cartFood={cartFood}
         setCartFood={setCartFood}
      />
    );
 })}

Logic for removing that particular item that is selected (which is not working and also bad solution since there can be case where you get same name and price twice)

const CartFood = ({
  foodName,
  foodPrice,
  numberOfPortions,
  cartFood,
  setCartFood,
}) => {
  const handleRemoveFood = () => {
    setCartFood(
      cartFood.filter(
        (el) =>
          el.foodName &&
          el.foodPrice !== cartFood.foodName &&
          cartFood.foodPrice
      )
    );
  };

  return (
    <div className='cartFood-container'>
      <p>{foodName}</p>
      <p>x{numberOfPortions}</p>
      <p>{foodPrice}kn</p>
      <p>
        <MdDeleteForever
          className='cartFood__icon'
          onClick={handleRemoveFood}
        />
      </p>
    </div>
  );
};

export default CartFood;

List of objects looks like this:

[{
  foodName: "Njoki with sos"
  foodPrice: 35
  numberOfPortions: 1
}, 
{
  foodName: "Chicken Wingos"
  foodPrice: 45
  numberOfPortions: 2
}]
Fraction
  • 11,668
  • 5
  • 28
  • 48
Matej
  • 49
  • 7
  • You think of this `const [cartFood, setCartFood] = useState([]);` or something else? – Matej Oct 06 '20 at 14:52
  • As long as your list items aren't overly large and your list is very long, you can also just compare the list items as string: cartFood.filter((el) => JSON.stringify(el) !== JSON.stringify(cartFood)) – Jan-Philipp Marks Oct 06 '20 at 15:00

3 Answers3

3
  • Put the index of the item in the array as the id. Pass it as your id.
 {cartFood.map((food, index) => {
              return (
                <CartFood
                  key={index}
                  id={index}
                  foodName={food.foodName}
                  foodPrice={food.foodPrice}
                  numberOfPortions={food.numberOfPortions}
                  cartFood={cartFood}
                  setCartFood={setCartFood}
                />
              );
            })}
  • Use the id to remove the food.
const CartFood = ({
  foodName,
  foodPrice,
  numberOfPortions,
  cartFood,
  setCartFood,
  id,
}) => {
  const handleRemoveFood = () => {
    setCartFood(cartFood.filter((el) => el.id !== id));
  };

  return (
    <div className='cartFood-container'>
      <p>{foodName}</p>
      <p>x{numberOfPortions}</p>
      <p>{foodPrice}kn</p>
      <p>
        <MdDeleteForever
          className='cartFood__icon'
          onClick={handleRemoveFood}
        />
      </p>
    </div>
  );
};

Prateek Thapa
  • 4,829
  • 1
  • 9
  • 23
  • Hm... I think I messed up something since ur soultion should work but everything from cart is removed, not just that specific item... – Matej Oct 06 '20 at 14:34
  • Yes, I tried to suggest edit but I couldint since it is only one charachter, problem continues, everything is removed from cart, and not only specific item, is it possible I messed something else? Do I need to spread something, maybe? – Matej Oct 06 '20 at 14:53
  • Do check again, the code should work. I've updated. You need to check the id which comes as the prop in your filter function. – Prateek Thapa Oct 06 '20 at 15:03
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/222604/discussion-between-matej-and-prateek-thapa). – Matej Oct 06 '20 at 15:06
  • @PrateekThapa It is not recommended to use indexes for keys if the order of items may change: https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318 – Fraction Oct 06 '20 at 15:12
  • @Fraction Understandable but since the owner of the code is a newbie and the item of an array really doesn't have a unique identity here. I admit this is an anti-pattern. But that shouldn't be a problem for this solution now. I don't think we should deep dive a newbie in what's right and wrong. Fixing an issue is a huge help I do admire you concern though. – Prateek Thapa Oct 06 '20 at 15:22
  • It's really a problem, especially in the case of deletion, which is what the OP is trying to do – Fraction Oct 06 '20 at 15:28
  • @Fraction I don't think so. The OP is updating the array in itself which causes the whole of array to change triggering a re-render. The architecture the OP has made with the component will have no effect on `index` as a key since, on every item update, the array itself changes. – Prateek Thapa Oct 06 '20 at 15:33
  • look at this answer: https://stackoverflow.com/a/56781201/11129751, maybe you understand the problem – Fraction Oct 06 '20 at 15:59
2

Something like this should work :

const handleRemoveFood = (obj) => {
        setCartFood((oldList) => oldList.filter((item) => item.foodName !== obj.foodName));
    };

Your button (icon) should call this function with current object data (obj)

A working example : https://codesandbox.io/s/cart-isz6c?file=/index.js

lucas24007
  • 93
  • 1
  • 6
  • I updated my code with @Prateek's solution, can I use id on function you showed here? – Matej Oct 06 '20 at 15:09
  • I tried, still same, nothing happens, that specific items still stay in cart, I dont know whats wrong... – Matej Oct 06 '20 at 15:12
1

From what I see in your repo:

  1. Just pass the food._id to FoodCard so you access it when you want to add or remove an item from cart:

FoodList.js

const foodList = (typeOfList) =>
  typeOfList.map(food => {
    return (
      <FoodCard
        key={food._id}
        foodId={food._id}
        foodName={food.title}
        foodPrice={food.price}
        foodPic={food.image}
        setCartFood={setCartFood}
        cartFood={cartFood}
      />
    );
  });

FoodCard.js

const handleAddToCard = () => {
    setCartFood([
      ...cartFood,
      {
        foodId,
        foodName,
        foodPrice,
        numberOfPortions,
      },
    ]);
  };

CartFood.js

const handleRemoveFood = () => {
    setCartFood(cartFood => cartFood.filter((el) => el.foodId !== foodId));
  };

Working example:

Edit crazy-browser-fhw9m

  1. You could use useReducer with useContext so you don't have to pass props down manually at every level, check this article for more info

  2. You don't need to pass the cartFood as a property just for updating the state since you can use setState callback:

setCartFood(cartFood => [
      ...cartFood,
      {
        foodId,
        foodName,
        foodPrice,
        numberOfPortions,
      },
    ]);
Fraction
  • 11,668
  • 5
  • 28
  • 48
  • Hm...I opened your example on codesandbox but when I wanna delete item from cart nothing happens :/ p.s. Thanks for article definetly gonna read it – Matej Oct 07 '20 at 09:09