Skip to content

Commit

Permalink
Improve the types for useOptimisticCart() (#2269)
Browse files Browse the repository at this point in the history
* Improve the types for `useOptimisticCart()`

* Fixes
  • Loading branch information
blittle authored Jul 9, 2024
1 parent ba5f528 commit 66236ca
Show file tree
Hide file tree
Showing 12 changed files with 45 additions and 27 deletions.
5 changes: 5 additions & 0 deletions .changeset/old-snails-care.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/hydrogen': patch
---

Improve the types for `useOptimisticCart()`
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ function CartAside({cart}: {cart: PageLayoutProps['cart']}) {
<Suspense fallback={<p>Loading cart ...</p>}>
<Await resolve={cart}>
{(cart) => {
return <CartMain cart={cart!} layout="aside" />;
return <CartMain cart={cart} layout="aside" />;
}}
</Await>
</Suspense>
Expand Down
2 changes: 1 addition & 1 deletion examples/legacy-customer-account-flow/app/routes/cart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export default function Cart() {
errorElement={<div>An error occurred</div>}
>
{(cart) => {
return <CartMain layout="page" cart={cart!} />;
return <CartMain layout="page" cart={cart} />;
}}
</Await>
</Suspense>
Expand Down
12 changes: 6 additions & 6 deletions examples/multipass/app/components/Cart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {MultipassCheckoutButton} from './MultipassCheckoutButton';
/***********************************************/

type CartMainProps = {
cart: CartApiQueryFragment;
cart: CartApiQueryFragment | null;
layout: 'page' | 'aside';
};

Expand All @@ -42,10 +42,10 @@ function CartDetails({
layout,
cart,
}: {
cart: OptimisticCart<CartApiQueryFragment>;
cart: OptimisticCart<CartApiQueryFragment | null>;
layout: 'page' | 'aside';
}) {
const cartHasItems = !!cart && cart.totalQuantity > 0;
const cartHasItems = cart?.totalQuantity && cart?.totalQuantity > 0;

return (
<div className="cart-details">
Expand Down Expand Up @@ -135,7 +135,7 @@ function CartLineItem({
);
}

function CartCheckoutActions({checkoutUrl}: {checkoutUrl: string}) {
function CartCheckoutActions({checkoutUrl}: {checkoutUrl?: string}) {
if (!checkoutUrl) return null;

/***********************************************/
Expand All @@ -155,7 +155,7 @@ export function CartSummary({
children = null,
}: {
children?: React.ReactNode;
cost: CartApiQueryFragment['cost'];
cost?: OptimisticCart<CartApiQueryFragment | null>['cost'];
layout: CartMainProps['layout'];
}) {
const className =
Expand Down Expand Up @@ -295,7 +295,7 @@ export function CartEmpty({
function CartDiscounts({
discountCodes,
}: {
discountCodes: CartApiQueryFragment['discountCodes'];
discountCodes?: CartApiQueryFragment['discountCodes'];
}) {
const codes: string[] =
discountCodes
Expand Down
2 changes: 1 addition & 1 deletion examples/multipass/app/components/PageLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ function CartAside({cart}: {cart: PageLayoutProps['cart']}) {
<Suspense fallback={<p>Loading cart ...</p>}>
<Await resolve={cart}>
{(cart) => {
return <CartMain cart={cart!} layout="aside" />;
return <CartMain cart={cart} layout="aside" />;
}}
</Await>
</Suspense>
Expand Down
2 changes: 1 addition & 1 deletion examples/multipass/app/routes/cart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export default function Cart() {
errorElement={<div>An error occurred</div>}
>
{(cart) => {
return <CartMain layout="page" cart={cart!} />;
return <CartMain layout="page" cart={cart} />;
}}
</Await>
</Suspense>
Expand Down
32 changes: 23 additions & 9 deletions packages/hydrogen/src/cart/optimistic/useOptimisticCart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,39 @@ import {
getOptimisticLineId,
isOptimisticLineId,
} from './optimistic-cart.helper';
import type {PartialDeep} from 'type-fest';
import type {CartReturn} from '../queries/cart-types';

export type OptimisticCartLine<T = CartLine> = T & {isOptimistic?: boolean};

export type OptimisticCart<T = CartReturn> = Omit<T, 'lines'> & {
isOptimistic?: boolean;
lines: {
nodes: Array<OptimisticCartLine>;
};
};
export type OptimisticCart<T = CartReturn> = T extends undefined | null
? // 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<OptimisticCartLine>;
};
} & Omit<PartialDeep<CartReturn>, 'lines'>
: Omit<T, 'lines'> & {
isOptimistic?: boolean;
lines: {
nodes: Array<OptimisticCartLine>;
};
};

/**
* @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<DefaultCart = CartReturn>(
cart?: DefaultCart,
): OptimisticCart<DefaultCart> {
export function useOptimisticCart<
DefaultCart = {
lines?: {
nodes: Array<{id: string; quantity: number; merchandise: {is: string}}>;
};
},
>(cart?: DefaultCart): OptimisticCart<DefaultCart> {
const fetchers = useFetchers();

if (!fetchers || !fetchers.length) return cart as OptimisticCart<DefaultCart>;
Expand Down
4 changes: 2 additions & 2 deletions templates/skeleton/app/components/CartMain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {CartSummary} from './CartSummary';
export type CartLayout = 'page' | 'aside';

export type CartMainProps = {
cart: CartApiQueryFragment;
cart: CartApiQueryFragment | null;
layout: CartLayout;
};

Expand All @@ -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! > 0;

return (
<div className={className}>
Expand Down
6 changes: 3 additions & 3 deletions templates/skeleton/app/components/CartSummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type {CartLayout} from '~/components/CartMain';
import {CartForm, Money, type OptimisticCart} from '@shopify/hydrogen';

type CartSummaryProps = {
cart: OptimisticCart<CartApiQueryFragment>;
cart: OptimisticCart<CartApiQueryFragment | null>;
layout: CartLayout;
};

Expand All @@ -29,7 +29,7 @@ export function CartSummary({cart, layout}: CartSummaryProps) {
</div>
);
}
function CartCheckoutActions({checkoutUrl}: {checkoutUrl: string}) {
function CartCheckoutActions({checkoutUrl}: {checkoutUrl?: string}) {
if (!checkoutUrl) return null;

return (
Expand All @@ -45,7 +45,7 @@ function CartCheckoutActions({checkoutUrl}: {checkoutUrl: string}) {
function CartDiscounts({
discountCodes,
}: {
discountCodes: CartApiQueryFragment['discountCodes'];
discountCodes?: CartApiQueryFragment['discountCodes'];
}) {
const codes: string[] =
discountCodes
Expand Down
2 changes: 1 addition & 1 deletion templates/skeleton/app/components/PageLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ function CartAside({cart}: {cart: PageLayoutProps['cart']}) {
<Suspense fallback={<p>Loading cart ...</p>}>
<Await resolve={cart}>
{(cart) => {
return <CartMain cart={cart!} layout="aside" />;
return <CartMain cart={cart} layout="aside" />;
}}
</Await>
</Suspense>
Expand Down
1 change: 0 additions & 1 deletion templates/skeleton/app/components/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,6 @@ export function PredictiveSearchForm({
{...props}
className={className}
onSubmit={(event) => {
debugger;
event.preventDefault();
event.stopPropagation();
if (!inputRef?.current || inputRef.current.value === '') {
Expand Down
2 changes: 1 addition & 1 deletion templates/skeleton/app/routes/cart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export default function Cart() {
errorElement={<div>An error occurred</div>}
>
{(cart) => {
return <CartMain layout="page" cart={cart!} />;
return <CartMain layout="page" cart={cart} />;
}}
</Await>
</Suspense>
Expand Down

0 comments on commit 66236ca

Please sign in to comment.