applyAttributeChange is undoing UI selections

dgriggak
Shopify Partner
2 0 0

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>
 
  )
}

 

 

 

Reply 1 (1)

dgriggak
Shopify Partner
2 0 0

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}>
/>