Skip to content

Commit

Permalink
feat: update scoped hooks (#202)
Browse files Browse the repository at this point in the history
* feat: add stand alone scoped hook for add product

* feat: add use products hook with filter, offset and limit

* feat: expose new cart scoped hooks

* feat: clean up cart provider

* refactor: use new scoped hooks

* chore: added changeset
  • Loading branch information
field123 authored Apr 30, 2024
1 parent cd24024 commit ce04040
Show file tree
Hide file tree
Showing 13 changed files with 225 additions and 112 deletions.
7 changes: 7 additions & 0 deletions .changeset/friendly-peas-mix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@elasticpath/react-shopper-hooks": minor
---

- Moved scoped hooks outside cart provider
- Added default cart id to cart provider using the sdks cart id
- Added useProducts hook
119 changes: 10 additions & 109 deletions packages/react-shopper-hooks/src/cart/cart-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,16 @@
import React, { createContext, ReactNode } from "react"
import {
Cart,
CartIncluded,
ResourceIncluded,
CartItem,
CartItemsResponse,
} from "@moltin/sdk"
import { Cart, CartIncluded, ResourceIncluded, CartItem } from "@moltin/sdk"
import { CartState } from "./types/cart-types"
import { enhanceCartResponse } from "./util/enhance-cart-response"
import { StoreEvent } from "../shared"
import { cartQueryKeys, useGetCart } from "./hooks/use-get-cart"
import { useUpdateCartItem } from "./hooks/use-update-cart-items"
import { useQueryClient } from "@tanstack/react-query"
import { useRemoveCartItem } from "./hooks/use-remove-cart-item"
import {
useAddBundleProductToCart,
useAddProductToCart,
useAddPromotionToCart,
useDeleteCartItems,
} from "./hooks"
import { useRemovePromotionCode } from "./hooks/use-remove-promotion"
import { useGetCart } from "./hooks/use-get-cart"
import { useElasticPath } from "../elasticpath"

export const CartItemsContext = createContext<
| ({
state: CartState | undefined
cartId?: string
cartId: string
emit?: (event: StoreEvent) => void
useScopedUpdateCartItem: () => ReturnType<typeof useUpdateCartItem>
useScopedRemoveCartItem: () => ReturnType<typeof useRemoveCartItem>
useScopedAddPromotion: () => ReturnType<typeof useAddPromotionToCart>
useScopedRemovePromotion: () => ReturnType<typeof useRemovePromotionCode>
useScopedAddProductToCart: () => ReturnType<typeof useAddProductToCart>
useScopedAddBundleProductToCart: () => ReturnType<
typeof useAddBundleProductToCart
>
useClearCart: () => ReturnType<typeof useDeleteCartItems>
} & Omit<ReturnType<typeof useGetCart>, "data">)
| undefined
>(undefined)
Expand All @@ -52,104 +28,29 @@ export function CartProvider({
initialState,
children,
emit,
cartId = "",
cartId: sourceCartId,
}: CartProviderProps) {
const queryClient = useQueryClient()
const { client } = useElasticPath()

const cartId = sourceCartId ?? client.Cart().cartId

const { data: rawCartData, ...rest } = useGetCart(cartId, {
initialData: initialState?.cart,
})

async function invalidateCartQuery() {
return queryClient.invalidateQueries({
queryKey: cartQueryKeys.detail(cartId),
})
}

function setCartQueryData(updatedData: CartItemsResponse) {
// Updates the cart items in the query cache
return queryClient.setQueryData(
cartQueryKeys.detail(cartId),
createCartItemsUpdater(updatedData.data),
)
}

const state =
rawCartData &&
enhanceCartResponse({
data: rawCartData,
included: rest.included,
})

const updateCartItem = () =>
useUpdateCartItem(cartId, {
onSuccess: (updatedData) => {
setCartQueryData(updatedData)
invalidateCartQuery()
},
})

const addProductToCart = () =>
useAddProductToCart(cartId, {
onSuccess: (updatedData) => {
setCartQueryData(updatedData)
invalidateCartQuery()
},
})

const removeCartItem = () =>
useRemoveCartItem(cartId, {
onSuccess: (updatedData) => {
setCartQueryData(updatedData)
invalidateCartQuery()
},
})

const addPromotion = () =>
useAddPromotionToCart(cartId, {
onSuccess: (updatedData) => {
setCartQueryData(updatedData)
invalidateCartQuery()
},
})

const removePromotion = () =>
useRemovePromotionCode(cartId, {
onSuccess: (updatedData) => {
setCartQueryData(updatedData)
invalidateCartQuery()
},
})

const addBundleItemToCart = () =>
useAddBundleProductToCart(cartId, {
onSuccess: (updatedData) => {
setCartQueryData(updatedData)
invalidateCartQuery()
},
})

const clearCart = () =>
useDeleteCartItems(cartId, {
onSuccess: async (updatedData) => {
setCartQueryData(updatedData)
await invalidateCartQuery()
},
})

return (
<CartItemsContext.Provider
value={{
state,
emit,
cartId: cartId ? cartId : undefined,
useScopedUpdateCartItem: updateCartItem,
useScopedRemoveCartItem: removeCartItem,
useScopedAddPromotion: addPromotion,
useScopedRemovePromotion: removePromotion,
useScopedAddProductToCart: addProductToCart,
useScopedAddBundleProductToCart: addBundleItemToCart,
useClearCart: clearCart,
cartId,
...rest,
}}
>
Expand All @@ -158,7 +59,7 @@ export function CartProvider({
)
}

function createCartItemsUpdater(updatedData: CartItem[]) {
export function createCartItemsUpdater(updatedData: CartItem[]) {
return function cartItemsUpdater(
oldData: ResourceIncluded<Cart, CartIncluded>,
) {
Expand Down
8 changes: 8 additions & 0 deletions packages/react-shopper-hooks/src/cart/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,11 @@ export * from "./use-add-bundle-product-to-cart"
export * from "./use-checkout"
export * from "./use-add-custom-item"
export * from "./use-delete-cart-items"
export * from "./use-cart-add-product"
export * from "./use-cart-remove-item"
export * from "./use-cart-update-item"
export * from "./use-cart-add-promotion"
export * from "./use-cart-add-bundle-item"
export * from "./use-cart-clear"
export * from "./use-cart-remove-promotion"
export * from "./use-cart-remove-item"
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createCartItemsUpdater, useCart } from "../cart-provider"
import { useQueryClient } from "@tanstack/react-query"
import { cartQueryKeys } from "./use-get-cart"
import { useAddBundleProductToCart } from "./use-add-bundle-product-to-cart"

export function useCartAddBundleItem() {
const { cartId } = useCart()
const queryClient = useQueryClient()
return useAddBundleProductToCart(cartId, {
onSuccess: (updatedData) => {
// Updates the cart items in the query cache
queryClient.setQueryData(
cartQueryKeys.detail(cartId),
createCartItemsUpdater(updatedData.data),
)
return queryClient.invalidateQueries({
queryKey: cartQueryKeys.detail(cartId),
})
},
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useAddProductToCart } from "./use-add-product"
import { createCartItemsUpdater, useCart } from "../cart-provider"
import { useQueryClient } from "@tanstack/react-query"
import { cartQueryKeys } from "./use-get-cart"

export function useCartAddProduct() {
const { cartId } = useCart()
const queryClient = useQueryClient()
return useAddProductToCart(cartId, {
onSuccess: (updatedData) => {
// Updates the cart items in the query cache
queryClient.setQueryData(
cartQueryKeys.detail(cartId),
createCartItemsUpdater(updatedData.data),
)
return queryClient.invalidateQueries({
queryKey: cartQueryKeys.detail(cartId),
})
},
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createCartItemsUpdater, useCart } from "../cart-provider"
import { useQueryClient } from "@tanstack/react-query"
import { cartQueryKeys } from "./use-get-cart"
import { useAddPromotionToCart } from "./use-add-promotion"

export function useCartAddPromotion() {
const { cartId } = useCart()
const queryClient = useQueryClient()
return useAddPromotionToCart(cartId, {
onSuccess: (updatedData) => {
// Updates the cart items in the query cache
queryClient.setQueryData(
cartQueryKeys.detail(cartId),
createCartItemsUpdater(updatedData.data),
)
return queryClient.invalidateQueries({
queryKey: cartQueryKeys.detail(cartId),
})
},
})
}
21 changes: 21 additions & 0 deletions packages/react-shopper-hooks/src/cart/hooks/use-cart-clear.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createCartItemsUpdater, useCart } from "../cart-provider"
import { useQueryClient } from "@tanstack/react-query"
import { cartQueryKeys } from "./use-get-cart"
import { useDeleteCartItems } from "./use-delete-cart-items"

export function useCartClear() {
const { cartId } = useCart()
const queryClient = useQueryClient()
return useDeleteCartItems(cartId, {
onSuccess: (updatedData) => {
// Updates the cart items in the query cache
queryClient.setQueryData(
cartQueryKeys.detail(cartId),
createCartItemsUpdater(updatedData.data),
)
return queryClient.invalidateQueries({
queryKey: cartQueryKeys.detail(cartId),
})
},
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createCartItemsUpdater, useCart } from "../cart-provider"
import { useQueryClient } from "@tanstack/react-query"
import { cartQueryKeys } from "./use-get-cart"
import { useRemoveCartItem } from "./use-remove-cart-item"

export function useCartRemoveItem() {
const { cartId } = useCart()
const queryClient = useQueryClient()
return useRemoveCartItem(cartId, {
onSuccess: (updatedData) => {
// Updates the cart items in the query cache
queryClient.setQueryData(
cartQueryKeys.detail(cartId),
createCartItemsUpdater(updatedData.data),
)
return queryClient.invalidateQueries({
queryKey: cartQueryKeys.detail(cartId),
})
},
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createCartItemsUpdater, useCart } from "../cart-provider"
import { useQueryClient } from "@tanstack/react-query"
import { cartQueryKeys } from "./use-get-cart"
import { useRemovePromotionCode } from "./use-remove-promotion"

export function useCartRemovePromotion() {
const { cartId } = useCart()
const queryClient = useQueryClient()
return useRemovePromotionCode(cartId, {
onSuccess: (updatedData) => {
// Updates the cart items in the query cache
queryClient.setQueryData(
cartQueryKeys.detail(cartId),
createCartItemsUpdater(updatedData.data),
)
return queryClient.invalidateQueries({
queryKey: cartQueryKeys.detail(cartId),
})
},
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createCartItemsUpdater, useCart } from "../cart-provider"
import { useQueryClient } from "@tanstack/react-query"
import { cartQueryKeys } from "./use-get-cart"
import { useUpdateCartItem } from "./use-update-cart-items"

export function useCartUpdateItem() {
const { cartId } = useCart()
const queryClient = useQueryClient()
return useUpdateCartItem(cartId, {
onSuccess: (updatedData) => {
// Updates the cart items in the query cache
queryClient.setQueryData(
cartQueryKeys.detail(cartId),
createCartItemsUpdater(updatedData.data),
)
return queryClient.invalidateQueries({
queryKey: cartQueryKeys.detail(cartId),
})
},
})
}
1 change: 1 addition & 0 deletions packages/react-shopper-hooks/src/product/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./use-product"
export * from "./use-products"
48 changes: 48 additions & 0 deletions packages/react-shopper-hooks/src/product/hooks/use-products.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useElasticPath } from "../../elasticpath/elasticpath"
import { UseQueryOptionsWrapper } from "../../types"
import type {
Moltin,
ShopperCatalogResourcePage,
ProductResponse,
} from "@moltin/sdk"
import { useQuery, UseQueryResult } from "@tanstack/react-query"
import { queryKeysFactory } from "../../shared/util/query-keys-factory"

const PRODUCTS_QUERY_KEY = "products" as const

export const productsQueryKeys = queryKeysFactory(PRODUCTS_QUERY_KEY)
type ProductsQueryKey = typeof productsQueryKeys

export type UseProductsParams = NonNullable<
Parameters<Moltin["ShopperCatalog"]["Products"]["All"]>
>[0]

export function useProducts(
params: UseProductsParams & {
limit?: number
offset?: number
filter?: object
},
options?: UseQueryOptionsWrapper<
ShopperCatalogResourcePage<ProductResponse>,
Error,
ReturnType<ProductsQueryKey["list"]>
>,
): UseQueryResult<ShopperCatalogResourcePage<ProductResponse>, Error> {
const { client } = useElasticPath()

const { limit = 25, offset = 0, filter = {} } = params

return useQuery({
queryKey: [
...productsQueryKeys.list({ limit, offset, filter, ...options }),
],
queryFn: () =>
client.ShopperCatalog.Products.Limit(limit)
.Offset(offset)
.Filter(filter)
.With(["main_image", "component_products", "files"])
.All(),
...options,
})
}
Loading

0 comments on commit ce04040

Please sign in to comment.