@storecraft/sdk-react-hooks
Version:
Official Storecraft Javascript SDK React Hooks
226 lines (199 loc) • 4.7 kB
JavaScript
/**
* @import {
* LineItem, ProductType, ShippingMethodType
* } from "@storecraft/core/api"
* @import { CartEvents, CartSubscriber, CartType } from "./use-cart.types.js"
*/
import { isEmpty } from "@storecraft/core/api/utils.func.js";
import { create_local_storage_hook, useStorecraft } from "@storecraft/sdk-react-hooks"
import React, { useCallback, useMemo } from "react"
/**
* @type {ReturnType<typeof create_local_storage_hook<CartType>>}
*/
const useCartState = create_local_storage_hook('storecraft_latest_cart');
/** @type {Set<CartSubscriber>} */
const subs = new Set();
/**
* @param {CartSubscriber} cb
*/
const subscribe = cb => {
subs.add(cb);
return () => {
subs.delete(cb)
}
}
/**
* @param {CartEvents} event
* @param {any} payload
*/
const notify = (event, payload) => {
// console.log(subs)
subs.forEach(
cb => cb(event, payload)
);
}
/**
* @returns {CartType}
*/
const create_new_cart = () => {
return {
line_items: [],
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
id: undefined,
}
}
/**
* @description useCart is a custom hook that manages
* the cart state.
*/
export const useCart = () => {
const {
state: cart,
setState: setCart,
} = useCartState(create_new_cart());
const {
sdk
} = useStorecraft();
/**
* @description Add Line iten with quantity to the cart
*/
const addLineItem = useCallback(
/**
* @param {ProductType} product
* @param {number} quantity
*/
(product, quantity = 1) => {
const item = cart.line_items.find(
(item) => (
(item.data.id === product.id) ||
(item.data.handle === product.handle)
)
);
if(item) {
item.qty = Math.min(
item.qty + quantity, item?.data?.qty ?? 0
);
} else {
cart.line_items.push({
id: product.id,
data: product,
qty: quantity
});
}
setCart(
{
...cart,
line_items: [...cart.line_items],
updated_at: new Date().toISOString()
}
);
notify('add_line_item');
}, [cart]
);
// console.log({cart})
/**
* @description Remove a line item from the cart
*/
const removeLineItem = useCallback(
/**
* @param {string} product_id_or_handle
*/
(product_id_or_handle='') => {
console.log({product_id_or_handle})
setCart(
(cart) => ({
...cart,
line_items: cart.line_items.filter(
(item) => (
(item.data.id !== product_id_or_handle) &&
(item.data.handle !== product_id_or_handle)
)
),
updated_at: new Date().toISOString()
})
);
notify('remove_line_item');
}, []
);
/**
* @description Update Quantity of an item in the cart
*/
const updateLineItem = useCallback(
/**
* @param {string} product_id_or_handle
* @param {number} [quantity=1]
*/
(product_id_or_handle='', quantity = 1) => {
const item = cart.line_items.find(
(item) => (
(item.data.id === product_id_or_handle) ||
(item.data.handle === product_id_or_handle)
)
);
if(!item) {
return;
}
if(quantity <= 0) {
removeLineItem(product_id_or_handle);
return;
}
item.qty = quantity;
setCart(
{
...cart,
line_items: [...cart.line_items],
updated_at: new Date().toISOString()
}
);
notify('update_line_item');
}, [cart, removeLineItem]
);
const clearCart = useCallback(
() => {
setCart(
create_new_cart()
);
notify('clear_cart');
}, [cart]
);
/**
* @description Quick Subtotal of the cart
* without calculating shipping, taxes and discounts.
* For better pricing, use the {@link pricing()} method.
*/
const quickSubTotal = useMemo(
() => cart.line_items.reduce(
(acc, item) => {
return acc + (item.data.price * item.qty);
}, 0
), [cart]
);
/**
* @description get the number of items in the cart,
* taking into account the quantity of each item.
*/
const itemsCount = useMemo(
() => cart.line_items.reduce(
(acc, item) => {
return acc + item.qty;
}, 0
), [cart]
);
return {
isEmpty: cart.line_items.length === 0,
cart,
itemsCount,
quickSubTotal,
actions: {
addLineItem,
removeLineItem,
updateLineItem,
clearCart,
},
events: {
subscribe,
notify,
}
}
}