Migrate to inventory actions and useInventory hook

Overview

This is a guide on how to migrate from manually accessing the product inventory to using the new approaches available:

useInventory hook.

The useInventory (Shopify / BigCommerce) hook can be used to retrieve inventory data on demand.

With the useInventory hook, we have access to the following properties:

name

type

description

availableForSale

boolean

It is used to check whether the product is available for sale.

quantity

number

The stock quantity of a given product.

minOrder

number

The minimum quantity required to buy the product.

maxOrder

number

The maximum quantity allowed to buy the product.

price

number

The product's price

useCartActions

The following actions are available when using the useCartActions:

  • isProductAvailableForSale
  • getProductQuantity
  • getProductMinOrder
  • getProductMaxOrder

Before these helpers were available you would access products within the inventory in the following way.

// Old way
import { useCartState } from 'frontend-checkout'

const { inventory } = useCartState()

if (inventory.status === 'loading') return

const variant = inventory.productVariants[variantId]

if (!variant) return

const isAvailableForSale = variant.isAvailableForSale
const quantity = variant.quantity
const minOrder = variant.minOrder
const maxOrder = variant.maxOrder

With the useInventory hook, you can get this information in the following way (check the useInventory-hook-complete-example.js tab below for a more thorough explanation):

import { useInventory } from 'frontend-checkout'

// If you are using Shopify's Storefront api
const id = 'Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc4NTc5ODkzODQ=' // storefrontId - GraphQL
// If you are using Shopify's REST api
const id = 6690750136476 // externalId - REST

// By passing an id and product type, useInventory will fetch the data for given ids
const { products, status } = useInventory({ ids: [id] })
const { availableForSale, quantity, minOrder, maxOrder, price } = products[id] || {}
import React, { useState, useEffect } from 'react'
import { useInventory } from 'frontend-checkout'

const IDLE = 'idle'
const LOADING = 'loading'
const SOLD_OUT = 'sold out'
const ERROR = 'error'

const AddToCartButton = ({
  // let's assume storefrontId is coming via props
  // from parent component, say ProductBox
  storefrontId,
}) => {
  // state to deal with button state
  const [buttonState, setButtonState] = useState(storefrontId ? LOADING : ERROR)
  // If you are using Shopify's REST api, you need to provide `externalId`
  // instead of `storefrontId`, and `productType`, something like this:
  // const { product, loading } = useInventory({ ids: [externalId], productType: 'Product' }) 
  const { products, status } = useInventory({ ids: [storefrontId] })
  const { availableForSale } = products[storefrontId] || {}
  
  useEffect(() => {
    if (status === LOADING || !storefrontId) return

    // if a product variant is available for sale,
    // we set the button state to IDLE, we can deal with that later in the code
    if (availableForSale) {
      return setButtonState(IDLE)
    }

    // if any of the above conditions are not true, we can assume that
    // the product is not available for sale, so we set the button state
    // to SOLD_OUT
    setButtonState(SOLD_OUT)

  }, [loading, storefrontId])

  // if the button state in place, feel free to use whatever approach you want
  // to handle the different button states you might have
  // for instance, something like this:
  return (
    <button
      onClick={someAction}
      disabled={buttonState === SOLD_OUT}
    >
      Add to Cart
    </button>
  )
}

With the useCartActions you can use the helpers to get this information from the inventory (check the usecartactions-complete-example.js tab below for a more thorough explanation) :

import { useCartState, useCartActions } from 'frontend-checkout'

const {
  isProductAvailableForSale,
  getProductQuantity,
  getProductMinOrder,
  getProductMaxOrder,
} = useCartActions()

// variantId can be one of the following:
// From Product.variants for both REST & GraphQL - Shopify
// - REST = LineItem.variant_id
// - GraphQL = LineItem.variant.id
const isAvailableForSale = await isProductAvailableForSale({ id: variantId })
const quantity = await getProductQuantity({ id: variantId })
const minOrder = await getProductMinOrder({ id: variantId })
const maxOrder = await getProductMaxOrder({ id: variantId })
import React, { useState, useEffect } from 'react'
import { useCartActions } from 'frontend-checkout'

const IDLE = 'idle'
const LOADING = 'loading'
const SOLD_OUT = 'sold out'
const ERROR = 'error'

const AddToCartButton = ({
  // let's assume storefrontId is coming via props
  // from parent component, say ProductBox
  storefrontId,
}) => {
  // state to deal with button state
  const [buttonState, setButtonState] = useState(storefrontId ? LOADING : ERROR)
  const { isProductAvailableForSale } = useCartActions()

  const checkAvailability = async () => {
    const isAvailable = await isProductAvailableForSale(storefrontId)

    // if a product variant is available for sale,
    // we set the button state to IDLE, we can deal with that later in the code
    if (isAvailable)
      return setButtonState(IDLE)
    }

    // if any of the above conditions are not true, we can assume that
    // the product is not available for sale, so we set the button state
    // to SOLD_OUT
    setButtonState(SOLD_OUT)
  }

  useEffect(() => {
    if (!storefrontId) return

    checkAvailability(storefrontId)
  }, [storefrontId])

  // if the button state in place, feel free to use whatever approach you want
  // to handle the different button states you might have
  // for instance, something like this:
  return (
    <button
      onClick={someAction}
      disabled={buttonState === SOLD_OUT}
    >
      Add to Cart
    </button>
  )
}

Comparing approaches

Approach using inventory directly

The cartState.inventory updates using the productInventory.json file generated by the backend, it contains all store products and productVariants and updates once in a minute.

🚧

To remove inventory delay, we are phasing out this approach after all the stores are migrated to one of the new approaches.

Why migrate?

  • By using this approach, we're loading data for all products even if we are on the product page where we need data only for a single product.
  • Data can be outdated, the delay between actual updates in inventory can be up to a minute.
  • We can't change the interval for productInventory.json file loading in the Section code.
  • Not recommended for stores with a large inventory.

Approach using the useInventory hook.

The useInventory hook makes an API request to get the same data but only for those products / productVariants that we need.

pros:

  • We load data only for those products that we need.
  • Data updates immediately when it has been changed on the admin.
  • We can set an optional interval that will make requests in the provided period to update data, or don't make it at all.

cons:

  • for Shopify product inventory data for minOrder and maxOrder will be updated only after site build since this data is from CMS not from the API, the rest of the fields (availableForSale, quantity, price) will be updated immediately.

Did this page help you?