← Volver a notas

2024-03-05 · published

Cart with Zustand

Cart with Zustand

    Creando el estado global

    Your store is a hook! You can put anything in it: primitives, objects, functions. The set function merges state.

    src/store/cart-store.ts

    // interfaz exclusiva del producto en el carrito,
    // porque no voy a querer almacenar toda la informacion
    interface CartProduct {
       id: string;
       slug: string;
       title: string;
       price: number;
       quantity: number;
       size: Size;
       image: string;
    }
    // interfaz del estado global
    interface CartState {
       // interfaz del carrito de compras (un arreglo de la interfaz definida anteriormente)
       cart: CartProduct[];
       // funcion de agregar un producto al carrito
       addProductToCart: (product: CartProduct) => void;
       // funcion para obtener la cantidad total diferente de productos
       // (no contando la cantidad de cada uno)
       getTotalItems: () => number;
       // funcion para simplemente quitar un producto del carrito
       removeProduct: (product: CartProduct) => void;
       // funcion para limpiar el carrito y dejarlo como un arreglo vacio
       clearCart: () => void;
       // funcion para actualizar la cantidad de un producto
       updateProductQuantity: (product: CartProduct, quantity: number) => void;
       getSummaryInformation: () => {
          subTotal: number;
          tax: number;
          itemsInCart: number;
          totalPrice: number;
       };
    }
    
    export const useCartStore = create<State>()(
       // persist nos permite almacenarlo en local storage de una manera sencilla
       persist(
          (set, get) => ({
             cart: [],
             addProductToCart: (product: CartProduct) => {
                // obtenemos el estado actual del carrito
                const { cart } = get();
                // segun el id del producto que recibimos, buscamos un boolean
                // que nos diga si ya tenemos algun producto en el carrito
                const productInCart = cart.some(
                   (item) => item.id === product.id && item.size === product.size
                );
                // si no lo tenemos simplemente lo agregamos
                if (!productInCart) {
                   set({ cart: [...cart, product] });
                   return;
                }
                // si lo tenemos nos encargamos de recorrer el carrito mediante un map
                // y cuando encontremos el id y el tamanio del producto aumentamos la cantidad
                const updatedCartProducts = cart.map((item) => {
                   if (item.id === product.id && item.size === product.size) {
                      return {
                         ...item,
                         quantity: item.quantity + product.quantity,
                      };
                   }
                   return item;
                });
                // seteamos el carrito con el carrito actualizado
                set({ cart: updatedCartProducts });
             },
             removeProduct: (product: CartProduct) => {
                // obtenemos el estado actual del carrito
                const { cart } = get();
                // creamos un nuevo arreglo sin el producto que recibimos
                // este nuevo arreglo contiene todos los productos anterioress
                // salvo los que coincidan con el id y el tamanio del producto que recibimos como parametro
                const updatedCartProducts = cart.filter(
                   (item) => item.id !== product.id || item.size !== product.size
                );
                // seteamos el carrito con el carrito actualizado
                set({ cart: updatedCartProducts });
             },
             clearCart: () => {
                // simplemente seteamos el carrito como un arreglo vacio
                set({ cart: [] });
             },
             getTotalItems: () => {
                // obtenemos el estado actual del carrito
                const { cart } = get();
                return cart.reduce((total, item) => total + item.quantity, 0);
             },
             updateProductQuantity: (product: CartProduct, quantity: number) => {
                // obtenemos el estado actual del carrito
                const { cart } = get();
                const updatedCartProducts = cart.map((item) => {
                   if (item.id === product.id && item.size === product.size) {
                      return {
                         ...item,
                         quantity: quantity,
                      };
                   }
                   return item;
                });
                // seteamos el carrito con el carrito actualizado
                set({ cart: updatedCartProducts });
             },
             getSummaryInformation: () => {
                // obtenemos el estado actual del carrito
                const { cart } = get();
    
                const subTotal = cart.reduce(
                   (subTotal, product) =>
                      product.quantity * product.price + subTotal,
                   0
                );
                const tax = subTotal * 0.15;
                const totalPrice = subTotal + tax;
                const itemsInCart = cart.reduce(
                   (total, item) => total + item.quantity,
                   0
                );
                return { subTotal, tax, itemsInCart, totalPrice };
             },
          }),
          {
             name: "shopping-cart",
          }
       )
    );

    Utilizando el estado en la aplicacion

    You can use the hook anywhere, without the need of providers. Select your state and the consuming component will re-render when that state changes.

    src/App.tsx

    const totalItemsInCart = useCartStore((state) => state.getTotalItems());
    const { itemsInCart, subTotal, tax, totalPrice } = useCartStore((state) =>
       state.getSummaryInformation()
    );
    const productsInCart = useCartStore((state) => state.cart);