thetrevorharmon / gatsby-theme-shopify-manager

The easiest way to start a Shopify shop on Gatsby.
https://gatsby-theme-shopify-manager.netlify.app/
MIT License
121 stars 11 forks source link

Cart "completedAt" check #60

Open schwartzmj opened 4 years ago

schwartzmj commented 4 years ago

I've noticed a "bug" in my use of gatsby-theme-shopify-manager related to the Cart "completedAt" check.

If a customer completes a checkout but still has the website open in a tab and goes back to the website, they will have a "completed" cart that they cannot get out of until they reload the website. This prevents the customer from adding new items to their cart because you cannot add items to a completed cart.

This happens because the only time the cart completedAt field is checked is when the app context is initiated (line 45 in ContextProvider.tsx).

We may want to add some functionality that checks if the cart is completed/valid before doing certain things like adding items to the cart.

nandorojo commented 4 years ago

Maybe you could redirect users to a custom thank-you page after they complete checkout, and have this page clear your cart.

1. Edit Shopify Checkout

In Shopify, go to Settings > Checkout, and in the Addition Scripts section, add your redirect URL. (credits to @mister-cairns for this.)

 <script type="text/javascript">
     var timeout = 6000; // you can change this timeout to your liking.
      setTimeout("location.href = 'https://your-url.com/thanks/';", timeout);
 </script>

2. Create a thank-you page on Gatsby

Make a static gastby page (like src/pages/thanks.) This thank you page will clear the cart on mount.

// src/pages/thanks.tsx
import { useClearCartOnMount } from '../hooks/use-clear-cart-on-mount'

export default function ThankYouPage() {
  useClearCartOnMount()

  return <div>Thanks for shopping with us!</div>
}

And then the React hook that makes the magic happen:

// src/hooks/use-clear-cart-on-mount.ts
import { useRemoveItemsFromCart, useCartItems } from 'gatsby-theme-shopify-manager'
import { useRef, useEffect } from 'react'

export const useClearCartOnMount = () => {
  const items = useCartItems()

  const removeItemsFromCart = useRemoveItemsFromCart()
  // this is a work-around, since the function isn't memoized
  const removeRef = useRef(useRemoveItemsFromCart)

  useEffect(() => {
    const cartItemVariantIds = items.map((variant) => variant.id)
    if (items.length > 0) {
      removeRef.current(cartItemVariantIds)
    }
  }, [items])
}

Since the functions aren't memoized, we'll have to use a useRef workaround. This work-around avoids infinite loops, ensuring we only clear the cart when the page mounts.

However, as seen by the [items] dependency in the useEffect function, we do run the effect again when the items array changes. We do this since the line items could be fetched asynchronously, and they might not be available on the first useEffect call.

Finally, if the cart has 0 items (i.e. items.length > 0 doesn't apply), nothing happens.

Edgecase

If a customer completes a checkout but still has the website open in a tab and goes back to the website, they will have a "completed" cart that they cannot get out of until they reload the website.

Interesting. My first thought is that you shouldn't open the checkout in a new window from your custom Gatsby cart. However, this edge case could still happen. You could maybe check if the checkout still exists when the window refocuses, and if not, clear the cart, using the method from above?