DEV Community

Rich11
Rich11

Posted on • Originally published at rileven.tech

Build an online Shop with Gatsby and Shopify (Part 3)

This is the third and last part of my gatsby and shopify series. If you have not read the parts before I would recommend reading them before. Otherwise, it will be hard to follow.
Today we will add the function to add a product to our cart and then build a checkout page where you can click on checkout to get redirected to your personalized shopify checkout.

First we should recall where we stopped.

import fetch from "isomorphic-fetch"
import React, { useEffect, createContext, useState } from "react"
import Client from "shopify-buy"

const client = Client.buildClient(
  {
    storefrontAccessToken: process.env.ACCESS_TOKEN,
    domain: `${process.env.SHOP_NAME}`,
  },
  fetch
)

const defaultValues = {
  client,
  checkout: {
    lineItems: [],
  },
}

export const StoreContext = createContext(defaultValues)
const isBrowser = typeof window !== "undefined"

const ContextProvider = ({ children }) => {
  const [checkout, setCheckout] = useState(defaultValues.checkout)

  const setCheckoutItem = checkout => {
    if (isBrowser) {
      localStorage.setItem("shopify_checkout_id", checkout.id)
    }

    setCheckout(checkout)
  }

  useEffect(() => {
    const initializeCheckout = async () => {
      const existingCheckoutID = isBrowser
        ? localStorage.getItem("shopify_checkout_id")
        : null

      if (existingCheckoutID && existingCheckoutID !== `null`) {
        try {
          const existingCheckout = await client.checkout.fetch(
            existingCheckoutID
          )
          if (!existingCheckout.completedAt) {
            setCheckoutItem(existingCheckout)
            return
          }
        } catch (e) {
          localStorage.setItem("shopify_checkout_id", null)
        }
      }

      const newCheckout = await client.checkout.create()
      setCheckoutItem(newCheckout)
    }

    initializeCheckout()
  }, [])

  return <StoreContext.Provider>{children}</StoreContext.Provider>
}

export default ContextProvider
Enter fullscreen mode Exit fullscreen mode

Add Variant to Cart

Next up we would need a function to go add a product to the cart. So let us build it.

const addVariantToCart = (variantId, quantity) => {
  const checkoutID = checkout.id

  const lineItemsToUpdate = [
    {
      variantId,
      quantity: parseInt(quantity, 10),
    },
  ]

  return client.checkout
    .addLineItems(checkoutID, lineItemsToUpdate)
    .then(res => {
      setCheckout(res)
    })
}
Enter fullscreen mode Exit fullscreen mode

The function takes two arguments. The first one is the id of the product or more specific: the id of the variant. That means when you have a product like a shirt you would have different variants.
You would take the id for the variant. The other parameter is the quantity.

Then afterwards we get the id for the checkout. This we need for later. It will be used for making our request because shopify needs to know to which checkout they need to add the variant to.
Then we construct or data we want to send. For now, we only send one at a time. Later you could make this more complex and maybe to a bulk adding version of this function.

In the last step we need to use our shopify sdk to add the items to the checkout. Then we wait for the response and set it to the state.

Passing our data

One important thing we missed in our ContextProvider file. We need to pass the functions and our data to use it afterwards.

return (
  <StoreContext.Provider
    value={{
      ...defaultValues,
      addVariantToCart,
      checkout,
    }}
  >
    {children}
  </StoreContext.Provider>
)
Enter fullscreen mode Exit fullscreen mode

We pass an object called value into the context provider. There we spread in the defaultValues. Pass our newly build function and the checkout state.

Now we could use our provider. 🚀

Using the provider

In order to make the data accessible everywhere in our pages I would recommend adding the ContextProvider component in our layout. Just wrap everything else in it. This makes it available everywhere else.

Now we could use the context everywhere you want.

We will add it to a component which shows a product with the add to cart button.

import React, { useContext } from "react"
import { StoreContext } from "../provider/ContextProvider"

const ProductDescription = ({ title, priceRange, description, shopifyId }) => {
  const { addVariantToCart, checkout } = useContext(StoreContext)

  const handleAddToCart = () => {
    addVariantToCart(shopifyId, 1)
  }

  const handleDirectCheckout = () => {
    addVariantToCart(shopifyId, 1)
    window.open(checkout.webUrl, "_self")
  }

  return (
    <div className="row">
      <div className="col">
        <StyledProductName>{title}</StyledProductName>
        <StyledPrice>{priceRange.minVariantPrice.amount} </StyledPrice>
        <StyledLabel>Product Type</StyledLabel>
        <StyledSelect name="producttype" id="producttype">
          <option value="Download">Download</option>
          <option value="Artwork">Artwork</option>
        </StyledSelect>
        <hr />
        <StyledProductText>{description}</StyledProductText>
        <StyledButton className="mr-3" onClick={handleAddToCart}>
          Add to Cart
        </StyledButton>
        <StyledButtonSecondary onClick={handleDirectCheckout}>
          Direct Checkout
        </StyledButtonSecondary>
      </div>
    </div>
  )
}

export default ProductDescription
Enter fullscreen mode Exit fullscreen mode

Do not wonder why the JSX Tags look a bit different. They are styled with styled components. Which is an entirely different topic. If you want to learn more about it. Just have a look here: Styled components.
For now, you just have to know that it is a way of styling and under the hood you could use any tag you want. And as we want to focus here on building the functionality this is not something you should worry about.

Enough about styled components. First we need to import the store context. Then we can use it in the component.

const { addVariantToCart, checkout } = useContext(StoreContext)
Enter fullscreen mode Exit fullscreen mode

We use the addVariantToCart function and the checkout state from the StoreContext. Above is shown how you get it from the context. Pretty easy right?

Next We have two functions which will handle the clicks on the buttons. The first just calls the addVariantToCart function with the shopifyId which was passed as props. The id you get with calling the shopify data on your page.
And then you could also add a quantity. For simplicity its only possible to add one product at the moment. But that would be an easy fix and could be done later.

Next the handleDirectCheckout function. This would be if a user directly wants to check out with the product on the page. Therefore, the addVariantToCart function is also called afterwards we open the shopify checkout.
The url of the checkout is stored in the checkout state. This is a large object we get from shopify. Also, there are other things in it like all the items in the line or the total amount of the cart.

Final words

Now you know how to use the context and how to build your own shop with shopify and gatsby. I know that's not all you need to build, but it should give you a starting point and hopefully
enables you to build your own shop. I think it is really fun and a cool way to build your custom shop.

If you have a question feel free to reach out. I would be happy to help.

Top comments (0)