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 |
|
| It is used to check whether the product is available for sale. |
|
| The stock quantity of a given product. |
|
| The product's price |
useCartActions
The following actions are available when using the useCartActions
:
isProductAvailableForSale
getProductQuantity
Before these helpers were available you would access products within the inventory in the following way.
deprecated-way.js
// 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
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):
using-useInventory-hook.js
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, price } = products[id] || {}
useInventory-hook-complete-example.js
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) :
using-useCartActions.js
import { useCartState, useCartActions } from 'frontend-checkout'
const {
isProductAvailableForSale,
getProductQuantity,
} = 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 })
useCartActions-complete-example.js
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:
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.