App reviews, troubleshooting, and recommendations
Recently ive started working on shopify apps rights now i am developing a checkout ui extension which lets the merchant to upsell products in checkout page. Last time ive checked everything was working fine but today iam facing some issues while i add up sell products to the cart line items when i add a product it was supposed to update the cart with updated line items but it works as expected when i click on add product 1st time the second time when i click it was supposed to check whether the product is already added in cart if yes it should only update the quantity + 1 this was working fine last week but today its not working and also if i add a new product its not appended to the line items instead it just simply replaces the product which was previously added.
documentation: https://shopify.dev/apps/checkout/product-offers/add-product-offer
App.jsx
import {useEffect, useState} from 'react';
import {
BlockStack,
useShop,
useCartLines,
useApplyCartLinesChange,
} from '@shopify/checkout-ui-extensions-react';
import {getRecommendations} from './services/recommendations.service.js';
import Loader from './components/Loader.jsx';
import {Divider, Heading} from '@shopify/checkout-ui-extensions';
import ProductCard from './components/ProductCard.jsx';
function App() {
const storeUrl = useShop().myshopifyDomain;
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(false);
const lineItems = useCartLines();
const applyCartLinesChange = useApplyCartLinesChange();
const recommendationLimit = 2;
console.log(lineItems, 'line items');
const lineItemsId = lineItems.map((l) =>
l.merchandise.product.id.substring(
l.merchandise.product.id.lastIndexOf('/') + 1
)
);
useEffect(async () => {
try {
setLoading(true);
let resp = await getRecommendations(
lineItemsId.toString(),
recommendationLimit,
storeUrl
);
setProducts(resp.data);
setLoading(false);
console.log(products, 'api response');
} catch (error) {
console.error(error);
}
}, []);
if (loading) return <Loader />;
if (!loading && products.length === 0) {
return null;
}
return (
<BlockStack spacing="loose">
<Divider />
<Heading level={2}>You might also like</Heading>
{products.map((p) => (
<ProductCard
key={p.id}
data={p}
applyCartLinesChange={applyCartLinesChange}
/>
))}
</BlockStack>
);
}
export default App;
<------------------------------------------->
Product card component
import {useEffect, useState} from 'react';
import {
Image,
Button,
InlineLayout,
BlockStack,
Text,
Banner,
useExtensionApi,
Select,
} from '@shopify/checkout-ui-extensions-react';
export default function ProductCard({data, applyCartLinesChange}) {
const {i18n} = useExtensionApi();
const [adding, setAdding] = useState(false);
const [selectedVariant, setSelectedVariant] = useState(0);
const [showError, setShowError] = useState(false);
const hasVariants = data.variants.length > 1;
useEffect(() => {
if (showError) {
const timer = setTimeout(() => setShowError(false), 3000);
return () => clearTimeout(timer);
}
}, [showError]);
function handelSelect(e) {
setSelectedVariant(e);
}
return (
<BlockStack spacing="loose">
<InlineLayout
spacing="base"
columns={[100, 'fill', 'auto']}
blockAlignment="center"
>
<Image
border="base"
borderWidth="base"
borderRadius="loose"
source={data.image.src}
description={data.title}
aspectRatio={1}
/>
<BlockStack spacing="none">
<Text size="medium" emphasis="strong">
{data.title}
</Text>
{hasVariants && (
<InlineLayout columns={[150, 'auto', 'auto']}>
<Select
label="Variant"
value={selectedVariant}
onChange={(e) => handelSelect(e)}
options={data.variants.map((val, i) => ({
value: i,
label: val.title.replace(/(<([^>]+)>)/gi, ''),
}))}
/>
</InlineLayout>
)}
<Text appearance="subdued">
{i18n.formatCurrency(data.variants[selectedVariant].price)}
</Text>
</BlockStack>
<Button
kind="secondary"
loading={adding}
accessibilityLabel={`Add ${data.title} to cart`}
onPress={async () => {
setAdding(true);
// Apply the cart lines change
const result = await applyCartLinesChange({
type: 'addCartLine',
merchandiseId: `gid://shopify/ProductVariant/${data.variants[selectedVariant].id}`,
quantity: 1,
});
setAdding(false);
if (result.type === 'error') {
setShowError(true);
console.error(result.message);
}
}}
>
Add
</Button>
</InlineLayout>
{showError && (
<Banner status="critical">
There was an issue adding this product. Please try again.
</Banner>
)}
</BlockStack>
);
}
Same issue here, even with sample code it is not working for me
Hey @skipper17 were you able to find a solution for this? Facing a similar issue 😞
I can confirm the issue.
const cartLines = useCartLines()
const lineChanger = useApplyCartLinesChange()
useEffect( () => {
if (!cartLines) return
lineChanger({
type: 'removeCartLine',
id: cartLines [0].id,
quantity: cartLines [0].quantity
})}
}, [cartLines])
This will not as expected empty the whole cart, but only run once, because the subscription does not trigger as one would expect.
Maybe @Shopify could give us an Idea of how these two interact.
I tested this and the checkout page updates the contents of the cart, but does not retrigger this.
Sooo,
I found the issue and a workaround:
The problem is that it seems they both run in the same step (react hooks) and the update is not set properly this way. My hotfix was to just create a dummy state to handle this:
const cartLines = useCartLines()
const lineChanger = useApplyCartLinesChange()
// we create a dummy State variable to update everything when we want to
const [ useless, setUseless ] = useState(0)
useEffect( () => { // do something with cartLines here }
, [useless]) // will trigger on any change to the dummy
// for example, this will empty the cart one by one, reloading once inbetween
useEffect( () => {
lineChanger( { /*change*/ }).then( () => {
setUseless( (i) => i+1 ) // set the useless State so the component will update
})
}, [useless] )
2m ago Learn the essential skills to navigate the Shopify admin with confidence. T...
By Shopify Feb 12, 2025Learn how to expand your operations internationally with Shopify Academy’s learning path...
By Shopify Feb 4, 2025Hey Community, happy February! Looking back to January, we kicked off the year with 8....
By JasonH Feb 3, 2025