React remove object from list of objects without unique value, using hooks

Viewed 1295

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
}]
3 Answers
  • 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>
  );
};

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

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,
      },
    ]);
Related