Search code examples
javascriptreactjstypescriptreact-nativezustand

can't synchronise the updated Products List array in zustand?


I want to explain the use case i have so you can understand it well

I have Cart & Products when the user adds the product (product_id = 1) twice to the cart and the product has the same options (red, xl) I increment the quantity,

if the user adds the same (product_id = 1) but with other options (green, xl), I add this product to the cart as a separate product.

Now this works well!

but when the user after added the above 2 products and add the same product with the same option (red, xl) again, It's added as a separated product!

What I expected if the product options existing before should increment the quantity otherwise added it as a separate.

What i tried

add the option ids into the Product property and check if it exists before or not then handle what I want but it does not work well and added the third product as a separate one!

Screen Record

code snippet

zustand Store

interface CartProductsProp extends State {
  cartProducts: ProductProps[];
  addToCart: (Products: ProductProps) => void;
  updateProductQuantity: (
    Product: ProductProps,
    updatedQuantity: number,
  ) => void;
  checkProductOptionsExist: (
    Product: ProductProps,
    updatedQuantity: number,
  ) => void;
  ...
}


export const useCartProduct = create<CartProductsProp>(
  persist(
    (set, get) => ({
      cartProducts: [],
      addToCart: (product) => {
        set((prev) => ({
          cartProducts: [...prev.cartProducts, {...product}],
        }));
      },
      checkProductOptionsExist: (
         product: ProductProps,
         updatedQuantity: number,
       ) => {
         set((prev) => {
           console.log('->prev', JSON.stringify(prev.cartProducts));
           console.log(
          'check==>IDs',
              cartProduct.id === product.id &&
              product.productOptionIds === cartProduct.productOptionIds,
           ); // for some reason this run towic when add the third product"with same options as first product "red,xl" true then false

           return prev.cartProducts.map((cartProduct) => {
             cartProduct.id === product.id &&
             product.productOptionIds === cartProduct.productOptionIds
               ? get().updateProductQuantity(product, updatedQuantity)
               : get().addToCart(product);
           });
         });
       },
       // To Update the quantity when product exist in cart before
       updateProductQuantity: (
         product: ProductProps,
         updatedQuantity: number,
       ) => {
         set((prev) => {
           let currentCart = prev.cartProducts.map((cartProduct) =>
             cartProduct.id === product.id &&
             areEqual(cartProduct.selectedOptions, product.selectedOptions)
               ? 
                 {
                  ...product,
                  quantity: cartProduct?.quantity! + updatedQuantity,
                  updated: 'yes@',
                  productTotalPrice:
                    (cartProduct?.quantity! + updatedQuantity) *
                    cartProduct.price,
                }
              : cartProduct,
            );
          console.log('##currentCart', JSON.stringify(currentCart));
          return {
            cartProducts: currentCart,
          };
        });
        ...
      },
    }),
    {
      name: 'cartListProduct-local',
      getStorage: () => AsyncStorage,
    },
  ),
);

Product Details

 const addProductToCart = () => {
      let productOptionIds = allOptions
        .map(({id}: {id: number | string}) => id)
        .sort()
        .join(',');
      let currentProduct = {
        ...item,
        id: item.id,
        product_id: item.id,
        quantity: currentQuantity,
        price: updatedPrice,
        productTotalPrice: updatedPrice * currentQuantity,
        selectedOptions: allOptions,
        productOptionIds: productOptionIds,
      };
      setAddToCartLoading(true);
      if (
        !cartProductList.some((alreadyExist) => alreadyExist.id === item.id)
      ) {
        addToCart(currentProduct);
        Alert.alert(t('addedSuccessfully'));
        setAddToCartLoading(false);
      } else {
        checkProductOptionsExist(currentProduct, currentQuantity);
        Alert.alert(t('addedSuccessfully'));
      }
  };

Utility

export const areEqual = (a: arrayProps = [], b: arrayProps = []): boolean => {
  // compare length of arrays
  if (a.length !== b.length) {
    return false;
  }
  // get ids set in b
  const idsSetInB = new Set(b.map(({id}: {id: number | string}) => id));
  console.log('idsSetInB', idsSetInB);
  // iterate over a, and check if the id of an item is not in b
  for (let {id} of a) {
    if (!idsSetInB.has(id)) {
      return false;
    }
  }
  // if it passes all the items, return true
  return true;
};

Solution

  • I just add the checks in the Product Details, not in the store,

    first, get the targeted product from the Cart so I can here check if it exists before or not based on the optionsIDs if the return undefined that's mean the product + options, not in the cart so I add it as a separated product otherwise I update the quantity and it works well.

    Maybe I can't do this in the store itself checkProductOptionsExist, (if u have any explanation tell me please)

    If u have an any better idea do it please ;)

    ...
    if (
        !cartProductList.some((alreadyExist) => alreadyExist.id === item.id)
      ) {
        addToCart(currentProduct);
        Alert.alert(t('addedSuccessfully'));
        setAddToCartLoading(false);
    }
    else {
            let res = cartProductList.find(
              (currentProd) =>
                currentProd.product_id === currentProduct.product_id &&
                currentProd.productOptionIds === currentProduct.productOptionIds, // or areEqual FC ;)
            );
            res != null
              ? updateProductQuantity(currentProduct, currentQuantity)
              : addToCart(currentProduct);
            setAddToCartLoading(false);
            Alert.alert(t('addedSuccessfully'));
      }
    

    store

    ....
    updateProductQuantity: (
            product: ProductProps,
            updatedQuantity: number,
          ) => {
            set((prev) => {
              let currentCart = prev.cartProducts.map((cartProduct) =>
                cartProduct.id === product.id &&
                areEqual(cartProduct.selectedOptions, product.selectedOptions)
                  ? {
                      ...product,
                      quantity: cartProduct?.quantity! + updatedQuantity,
                      productTotalPrice:
                        (cartProduct?.quantity! + updatedQuantity) *
                        cartProduct.price,
                    }
                  : cartProduct,
              );
              return {
                cartProducts: currentCart,
              };
            });
          },
    ....