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 ;
}}