From 2bf295e7a6c8104961426389ba3eb7090622b41f Mon Sep 17 00:00:00 2001 From: Bret Little Date: Fri, 21 Jun 2024 11:01:19 -0400 Subject: [PATCH] Improve the types for `useOptimisticCart()` --- .changeset/old-snails-care.md | 5 ++++ .../app/components/PageLayout.tsx | 2 +- .../app/routes/cart.tsx | 2 +- examples/multipass/app/components/Cart.tsx | 13 +++++----- .../multipass/app/components/PageLayout.tsx | 2 +- examples/multipass/app/routes/cart.tsx | 2 +- .../src/cart/optimistic/useOptimisticCart.tsx | 24 +++++++++++++------ .../skeleton/app/components/CartMain.tsx | 4 ++-- .../skeleton/app/components/CartSummary.tsx | 6 ++--- .../skeleton/app/components/PageLayout.tsx | 2 +- templates/skeleton/app/components/Search.tsx | 1 - templates/skeleton/app/routes/cart.tsx | 2 +- 12 files changed, 40 insertions(+), 25 deletions(-) create mode 100644 .changeset/old-snails-care.md diff --git a/.changeset/old-snails-care.md b/.changeset/old-snails-care.md new file mode 100644 index 0000000000..3b27f8eb96 --- /dev/null +++ b/.changeset/old-snails-care.md @@ -0,0 +1,5 @@ +--- +'@shopify/hydrogen': patch +--- + +Improve the types for `useOptimisticCart()` diff --git a/examples/legacy-customer-account-flow/app/components/PageLayout.tsx b/examples/legacy-customer-account-flow/app/components/PageLayout.tsx index e492066f86..ac9c2f43c1 100644 --- a/examples/legacy-customer-account-flow/app/components/PageLayout.tsx +++ b/examples/legacy-customer-account-flow/app/components/PageLayout.tsx @@ -64,7 +64,7 @@ function CartAside({cart}: {cart: PageLayoutProps['cart']}) { Loading cart ...

}> {(cart) => { - return ; + return ; }}
diff --git a/examples/legacy-customer-account-flow/app/routes/cart.tsx b/examples/legacy-customer-account-flow/app/routes/cart.tsx index 9dc63816eb..4fc07e0875 100644 --- a/examples/legacy-customer-account-flow/app/routes/cart.tsx +++ b/examples/legacy-customer-account-flow/app/routes/cart.tsx @@ -105,7 +105,7 @@ export default function Cart() { errorElement={
An error occurred
} > {(cart) => { - return ; + return ; }} diff --git a/examples/multipass/app/components/Cart.tsx b/examples/multipass/app/components/Cart.tsx index b159ea0657..2dbd744018 100644 --- a/examples/multipass/app/components/Cart.tsx +++ b/examples/multipass/app/components/Cart.tsx @@ -9,6 +9,7 @@ import { import type {CartLineUpdateInput} from '@shopify/hydrogen/storefront-api-types'; import {Link} from '@remix-run/react'; import type {CartApiQueryFragment} from 'storefrontapi.generated'; +import type {PartialDeep} from 'type-fest'; import {useVariantUrl} from '~/lib/variants'; /***********************************************/ /********** EXAMPLE UPDATE STARTS ************/ @@ -17,7 +18,7 @@ import {MultipassCheckoutButton} from './MultipassCheckoutButton'; /***********************************************/ type CartMainProps = { - cart: CartApiQueryFragment; + cart: CartApiQueryFragment | null; layout: 'page' | 'aside'; }; @@ -42,10 +43,10 @@ function CartDetails({ layout, cart, }: { - cart: OptimisticCart; + cart: OptimisticCart; layout: 'page' | 'aside'; }) { - const cartHasItems = !!cart && cart.totalQuantity > 0; + const cartHasItems = cart?.totalQuantity && cart?.totalQuantity > 0; return (
@@ -135,7 +136,7 @@ function CartLineItem({ ); } -function CartCheckoutActions({checkoutUrl}: {checkoutUrl: string}) { +function CartCheckoutActions({checkoutUrl}: {checkoutUrl?: string}) { if (!checkoutUrl) return null; /***********************************************/ @@ -155,7 +156,7 @@ export function CartSummary({ children = null, }: { children?: React.ReactNode; - cost: CartApiQueryFragment['cost']; + cost?: PartialDeep; layout: CartMainProps['layout']; }) { const className = @@ -295,7 +296,7 @@ export function CartEmpty({ function CartDiscounts({ discountCodes, }: { - discountCodes: CartApiQueryFragment['discountCodes']; + discountCodes?: CartApiQueryFragment['discountCodes']; }) { const codes: string[] = discountCodes diff --git a/examples/multipass/app/components/PageLayout.tsx b/examples/multipass/app/components/PageLayout.tsx index ef52df22a2..6433883122 100644 --- a/examples/multipass/app/components/PageLayout.tsx +++ b/examples/multipass/app/components/PageLayout.tsx @@ -64,7 +64,7 @@ function CartAside({cart}: {cart: PageLayoutProps['cart']}) { Loading cart ...

}> {(cart) => { - return ; + return ; }}
diff --git a/examples/multipass/app/routes/cart.tsx b/examples/multipass/app/routes/cart.tsx index c6949a6f52..831ef58eff 100644 --- a/examples/multipass/app/routes/cart.tsx +++ b/examples/multipass/app/routes/cart.tsx @@ -105,7 +105,7 @@ export default function Cart() { errorElement={
An error occurred
} > {(cart) => { - return ; + return ; }} diff --git a/packages/hydrogen/src/cart/optimistic/useOptimisticCart.tsx b/packages/hydrogen/src/cart/optimistic/useOptimisticCart.tsx index cb513448e7..fe3aa99ff7 100644 --- a/packages/hydrogen/src/cart/optimistic/useOptimisticCart.tsx +++ b/packages/hydrogen/src/cart/optimistic/useOptimisticCart.tsx @@ -8,23 +8,33 @@ import { getOptimisticLineId, isOptimisticLineId, } from './optimistic-cart.helper'; +import type {PartialDeep} from 'type-fest'; import type {CartReturn} from '../queries/cart-types'; export type OptimisticCartLine = T & {isOptimistic?: boolean}; -export type OptimisticCart = Omit & { - isOptimistic?: boolean; - lines: { - nodes: Array; - }; -}; +export type OptimisticCart = T extends {} + ? Omit & { + isOptimistic?: boolean; + lines: { + nodes: Array; + }; + } + : // This is the null/undefined case, where the cart has yet to be created. + // But we still need to provide an optimistic cart object. + { + isOptimistic?: boolean; + lines: { + nodes: Array; + }; + } & Omit, 'lines'>; /** * @param cart The cart object from `context.cart.get()` returned by a server loader. * * @returns A new cart object augmented with optimistic state. Each cart line item that is optimistically added includes an `isOptimistic` property. Also if the cart has _any_ optimistic state, a root property `isOptimistic` will be set to `true`. */ -export function useOptimisticCart( +export function useOptimisticCart>( cart?: DefaultCart, ): OptimisticCart { const fetchers = useFetchers(); diff --git a/templates/skeleton/app/components/CartMain.tsx b/templates/skeleton/app/components/CartMain.tsx index 672486ac93..02e0ce9773 100644 --- a/templates/skeleton/app/components/CartMain.tsx +++ b/templates/skeleton/app/components/CartMain.tsx @@ -8,7 +8,7 @@ import {CartSummary} from './CartSummary'; export type CartLayout = 'page' | 'aside'; export type CartMainProps = { - cart: CartApiQueryFragment; + cart: CartApiQueryFragment | null; layout: CartLayout; }; @@ -26,7 +26,7 @@ export function CartMain({layout, cart: originalCart}: CartMainProps) { cart && Boolean(cart?.discountCodes?.filter((code) => code.applicable)?.length); const className = `cart-main ${withDiscount ? 'with-discount' : ''}`; - const cartHasItems = !!cart && cart.totalQuantity > 0; + const cartHasItems = cart?.totalQuantity && cart.totalQuantity > 0; return (
diff --git a/templates/skeleton/app/components/CartSummary.tsx b/templates/skeleton/app/components/CartSummary.tsx index 853fdce848..cfd15a8979 100644 --- a/templates/skeleton/app/components/CartSummary.tsx +++ b/templates/skeleton/app/components/CartSummary.tsx @@ -3,7 +3,7 @@ import type {CartLayout} from '~/components/CartMain'; import {CartForm, Money, type OptimisticCart} from '@shopify/hydrogen'; type CartSummaryProps = { - cart: OptimisticCart; + cart: OptimisticCart; layout: CartLayout; }; @@ -29,7 +29,7 @@ export function CartSummary({cart, layout}: CartSummaryProps) {
); } -function CartCheckoutActions({checkoutUrl}: {checkoutUrl: string}) { +function CartCheckoutActions({checkoutUrl}: {checkoutUrl?: string}) { if (!checkoutUrl) return null; return ( @@ -45,7 +45,7 @@ function CartCheckoutActions({checkoutUrl}: {checkoutUrl: string}) { function CartDiscounts({ discountCodes, }: { - discountCodes: CartApiQueryFragment['discountCodes']; + discountCodes?: CartApiQueryFragment['discountCodes']; }) { const codes: string[] = discountCodes diff --git a/templates/skeleton/app/components/PageLayout.tsx b/templates/skeleton/app/components/PageLayout.tsx index 802392724e..8804aa8f7e 100644 --- a/templates/skeleton/app/components/PageLayout.tsx +++ b/templates/skeleton/app/components/PageLayout.tsx @@ -60,7 +60,7 @@ function CartAside({cart}: {cart: PageLayoutProps['cart']}) { Loading cart ...

}> {(cart) => { - return ; + return ; }}
diff --git a/templates/skeleton/app/components/Search.tsx b/templates/skeleton/app/components/Search.tsx index 8d7828a234..179dcb4045 100644 --- a/templates/skeleton/app/components/Search.tsx +++ b/templates/skeleton/app/components/Search.tsx @@ -309,7 +309,6 @@ export function PredictiveSearchForm({ {...props} className={className} onSubmit={(event) => { - debugger; event.preventDefault(); event.stopPropagation(); if (!inputRef?.current || inputRef.current.value === '') { diff --git a/templates/skeleton/app/routes/cart.tsx b/templates/skeleton/app/routes/cart.tsx index f4a36e1584..da3d14f080 100644 --- a/templates/skeleton/app/routes/cart.tsx +++ b/templates/skeleton/app/routes/cart.tsx @@ -93,7 +93,7 @@ export default function Cart() { errorElement={
An error occurred
} > {(cart) => { - return ; + return ; }}