Frontend

#20 Update cart items quantity

Julian Mayorga
Instructor
Julian
Jamie Barton
Instructor
Jamie
githubSource code

You will allow users to add and remove items from their cart.

To achieve this, you will first write a GraphQL document to increase and decrease cart items. Then create icons to put on the buttons that update items quantity. And finally you will use the autogenerated mutation hooks in the cart item component.

Create GraphQL documents to increase and decrease cart items

Create two documents on the documents folder to call the increaseCartItem and decreaseCartItem mutations.

First create IncreaseCartItem.graphql:

# import Cart from "./CartFragment"

mutation increaseCartItem($input: IncreaseCartItemInput!) {
  increaseCartItem(input: $input) {
    ...Cart
  }
}

The create DecreaseCartItem.graphql:

# import Cart from "./CartFragment"

mutation decreaseCartItem($input: DecreaseCartItemInput!) {
  decreaseCartItem(input: $input) {
    ...Cart
  }
}

Finally run yarn codegen to update types.ts.

Create plus and minus icon components

Now you will create two icons to place on the update quantity buttons.

They will be outlined plus and minus icons, taken from the awesomely handy heroicons library.

Create a file inside components called PlusIcon.tsx:

export function PlusIcon() {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      className="h-6 w-6"
      fill="none"
      viewBox="0 0 24 24"
      stroke="currentColor"
    >
      <path
        strokeLinecap="round"
        strokeLinejoin="round"
        strokeWidth={2}
        d="M12 4v16m8-8H4"
      />
    </svg>
  );
}

Now create MinusIcon.tsx:

export function MinusIcon() {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      className="h-6 w-6"
      fill="none"
      viewBox="0 0 24 24"
      stroke="currentColor"
    >
      <path
        strokeLinecap="round"
        strokeLinejoin="round"
        strokeWidth={2}
        d="M20 12H4"
      />
    </svg>
  );
}

Import and use mutation hooks in cart item

With both hooks and icons, you have all the tools you need to add two update quantity buttons to CartItem.

Call both useDecreaseCartItemMutation and useIncreaseCartItemMutation on the top of the component, and make them refetch the GetCartDocument query once their mutations finish.

After that add an increase and a decrease item buttons, which will call the increaseCartItem and decreaseCartItem functions that the hooks returned. You will also add a disabled property to them so that they are non clickable when the associated mutation is loading.

This is what CartItem.tsx should look like with those additions:

import Image from "next/image";
import {
  CartItem,
  GetCartDocument,
  useDecreaseCartItemMutation,
  useIncreaseCartItemMutation,
} from "../types";
import { MinusIcon } from "./MinusIcon";
import { PlusIcon } from "./PlusIcon";

export function CartItem({ item, cartId }: { item: CartItem; cartId: string }) {
  const [increaseCartItem, { loading: increasingCartItem }] =
    useIncreaseCartItemMutation({
      refetchQueries: [GetCartDocument],
    });
  const [decreaseCartItem, { loading: decreasingCartItem }] =
    useDecreaseCartItemMutation({
      refetchQueries: [GetCartDocument],
    });
  return (
    <div>
      {`//...`}
      <div className="flex gap-2">
        <div className="flex-1 flex">
          <div className="px-2 py-1 font-light border border-neutral-700 flex-1">
            {item.quantity}
          </div>
          <button
            onClick={() =>
              decreaseCartItem({
                variables: { input: { id: item.id, cartId } },
              })
            }
            disabled={decreasingCartItem}
            className="p-1 font-light border border-neutral-700  hover:bg-black hover:text-white"
          >
            <MinusIcon />
          </button>
          <button
            onClick={() =>
              increaseCartItem({
                variables: { input: { id: item.id, cartId } },
              })
            }
            disabled={increasingCartItem}
            className="p-1 font-light border border-neutral-700  hover:bg-black hover:text-white"
          >
            <PlusIcon />
          </button>
        </div>
      </div>
    </div>
  );
}

Make sure to update CartDetail with the new cartId required property in CartItem.

CartDetail.tsx

<CartItem key={item.id} item={item} cartId={cart.id} />

Try the new update quantity flow by navigating to the cart summary page and adding and removing items.