For discussing the development and usage of Checkout UI extensions, post-purchase extensions, web pixels, Customer Accounts UI extensions, and POS UI extensions
I'm building a Checkout Extension to include some Questions as part of the checkout flow. When the user answers the question I need to add the value to the CheckoutAttributes for use post checkout.
The normal event handlers work fine but as soon as I include the applyAttributeChange , every time the event handler is called the UI input (Select or Radio) reverts back to the unselected state instantly. What am I doing wrong?
import React, { useState, useEffect } from 'react';
import {
BlockStack,
BlockSpacer,
Choice,
Heading,
Select,
Text,
reactExtension,
useApplyAttributeChange
} from '@shopify/ui-extensions-react/checkout';
import { ChoiceList, InlineStack } from '@shopify/ui-extensions/checkout';
export default reactExtension(
'purchase.checkout.contact.render-after',
() => <Extension />,
);
function Extension() {
const applyAttributeChange =
useApplyAttributeChange();
function changeFirstPurchase(value){
console.log(`change first purhcase ${value}`);
const result = applyAttributeChange({
key: 'firstPurchase',
type: 'updateAttribute',
value: value,
});
}
function onPrimaryActivityChange(value){
console.log(`update registration info ${value}`);
const result = applyAttributeChange({
key: 'primaryActivity',
type: 'updateAttribute',
value: value,
});
}
return (
<BlockStack>
<BlockSpacer></BlockSpacer>
<Heading level="1">Registration Information</Heading>
<Text>What is the primary activity you will use this product for?</Text>
<Select
id="registrationPrimaryActivity"
label="Primary Activity"
value=""
onChange={onPrimaryActivityChange}
options={[
{
value: '',
label: '',
},
{
value: 'astronomy',
label: 'Astronomy',
},
{
value: 'backyard',
label: 'Backyard',
},
{
value: 'birding',
label: 'birding',
},
{
value: 'boating',
label: 'Boating',
},
{
value: 'camping',
label: 'Camping',
},
{
value: 'gardening',
label: 'Gardening',
},
{
value: 'golfing',
label: 'Golfing',
}
]}
/>
<Text>Is this your first purchase?</Text>
<InlineStack>
<ChoiceList
id="registrationFirstPurchase"
name="firstPurchase"
value=""
onChange={changeFirstPurchase}>
<Choice id="firstPurchaseYes">Yes</Choice>
<Choice id="firstPurchaseNo">No</Choice>
</ChoiceList>
</InlineStack>
</BlockStack>
)
}
I found the answer to this. In the Shopify docs it mentions that the Form UI components are controlled components, https://shopify.dev/docs/api/checkout-ui-extensions/2024-04/components/forms/select which means the state needs to be saved outside of the component in order to access and work with it.
This React document helps explain how to solve the issue https://legacy.reactjs.org/docs/forms.html#controlled-components
The new code ends up looking like this using the 'useState' and 'setState'
function Extension() {
const applyAttributeChange = useApplyAttributeChange();
const [primaryActivity, setPrimaryActivity] = useState('');
const [firstPurchase, setFirstPurchase] = useState('')
function changeFirstPurchase(value){
setFirstPurchase(value);
const result = applyAttributeChange({
key: 'firstPurchase',
type: 'updateAttribute',
value: firstPurchase,
});
}
function onPrimaryActivityChange(value){
setPrimaryActivity(value);
const result = applyAttributeChange({
key: 'primaryActivity',
type: 'updateAttribute',
value: value,
});
}
and for the component the value needs to be bound to the class's state property
<Select
id="registrationPrimaryActivity"
label="Primary Activity"
name="primaryActivity"
value={primaryActivity}
onChange={onPrimaryActivityChange}>
/>