skip to Main Content

When I’m trying to update state in react application, I’m getting undefined as a result. I’m basically trying to remove an item from a list component, and trying to update the state, by getting the previous state. But when I try to remove the item, I’m getting undefined.

Now, I have this code:


const Checkout = () => {
  const [products, setProducts] = useState([]);
  const [loading, setLoading] = useState(true);
  const [finalPrice, setFinalPrice] = useState(0);

  const { basketItems, setBasketItems } = useContext(ProductTypeContext);

  useEffect(() => {
    basketItems &&
      basketItems.forEach((id) => {
        axios
          .get(`http://localhost:6969/api/products/${id}`)
          .then((data) => {
            setProducts((prevState) => [...prevState, data.data.product]);
            setFinalPrice((prevState) => Number(prevState) + Number(data.data.product.price));
            setTimeout(() => {
              setLoading(false);
            }, 300);
          })
          .catch((err) => console.log(err));
      });
  }, []);

  if (loading) {
    return <div>Loading...</div>;
  }

  return (
    <>
      <div className="checkout-container">
        {products.map((product, index) => (
          <div key={index} className="checkout-products">
            <img src={product.img} width="100px" />
            <div>{product.name}</div>
            <div>{product.price}</div>
            <div>{product.taste}</div>
            <span
              onClick={() => {
                setBasketItems((prevState) => {
                  [...prevState, prevState.splice(index, 1)];
                });
              }}
            >
              X
            </span>
          </div>
        ))}
      </div>
      <div>Final Price: {finalPrice}</div>
    </>
  );
};

export default Checkout;

And I guess something is wrong when I click the X button, that is updating the state. Any sugesstions?

2

Answers


  1. The approach I’d use to handle this is to use the filter method instead of splice.

    I’ll create a function called removeProduct with a param productId and then filter out that ID from the array.

    // Note: You will have to adjust the code if the project doesn’t have an ID. You will have to pass in a unique field and adjust it

    const removeProduct = (productId) => {
        setBasketItems((ps) => ps.filter((item) => productId !== item.id));
     };
    
    

    I’ll also pass the function to the onClick of the X button.

    <span onClick={() => removeProduct(product.id)}>
      X
    </span>
    
    

    Using setTimeout is not efficient in this usecase.

     setTimeout(() => {
       setLoading(false);
     }, 300);
    
    

    Here’s the complete code

    const Checkout = () => {
      const [products, setProducts] = useState([]);
      const [loading, setLoading] = useState(true);
      const [finalPrice, setFinalPrice] = useState(0);
    
      const { basketItems, setBasketItems } = useContext(ProductTypeContext);
    
      useEffect(() => {
        basketItems &&
          basketItems.forEach((id) => {
            axios
              .get(`http://localhost:6969/api/products/${id}`)
              .then((data) => {
                setProducts((prevState) => [...prevState, data.data.product]);
                setFinalPrice((prevState) => Number(prevState) + Number(data.data.product.price));
                setLoading(false);
              })
              .catch((err) => console.log(err));
          });
      }, []);
    
      const removeProduct = (productId) => {
        setBasketItems((ps) => ps.filter((item) => productId !== item.id));
      };
    
      if (loading) {
        return <div>Loading...</div>;
      }
    
      return (
        <>
          <div className="checkout-container">
            {products.map((product, index) => (
              <div key={index} className="checkout-products">
                <img src={product.img} width="100px" />
                <div>{product.name}</div>
                <div>{product.price}</div>
                <div>{product.taste}</div>
                <span
                  onClick={() => removeProduct(product.id)}
                >
                  X
                </span>
              </div>
            ))}
          </div>
          <div>Final Price: {finalPrice}</div>
        </>
      );
    };
    
    export default Checkout;
    
    
    Login or Signup to reply.
  2. The splice method returns the removed elements, not the modified array.

    Update this part:

    <span
    onClick={() => {
        setBasketItems((prevState) => {
        const newState = [...prevState];
        newState.splice(index, 1);
        return newState;
        });
    }}
    >
    X
    </span>
    

    Note that: In your code, Final Price only updates when fetching data. You can create a function to calculate the final price based on the current products array and call it when removing an item.

    Update Final Price Function:

    const calculateFinalPrice = (products) => {
      return products.reduce((total, product) => total + Number(product.price), 0);
    };
    

    Use this function on our updated code:

    <span
    onClick={() => {
        setBasketItems((prevState) => {
        const newState = [...prevState];
        newState.splice(index, 1);
        return newState;
        });
        setFinalPrice((prevState) => calculateFinalPrice(prevState.splice(index, 1)));
    }}
    >
    X
    </span>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search