Have your say in Community Polls: What was/is your greatest motivation to start your own business?
Our Partner & Developer boards on the community are moving to a brand new home: the .dev community forums! While you can still access past discussions here, for all your future app and storefront building questions, head over to the new forums.

useAppMetafields error in a custom hook

useAppMetafields error in a custom hook

rndgf
Shopify Partner
2 0 3

Hi there, i'm facing an understandable issue in my app.
I'm building an app using checkout-ui extensions and functions.
Several of them are sharing the same configuration values such as third-party url, properties etc.

To make that app more manageable, i store this config in a metafield on my shop.
Metafield retrieved with useAppMetafields in my extensions.

That's for the context.

Now, the issue.

To make this configuration fetch/init more manageable, i dev a custom hook and intend to call it in each extension that need to access the config values.
It's this hook which do use the useAppMetafields hook.

Here is the code :

import { useState, useEffect } from "react";
import { useAppMetafields } from "@shopify/ui-extensions-react/checkout";

type Configuration = {
  debugMode: boolean;
  middlewareUrl: string;
  middlewareToken: string;
  cartPropertiesPrefix: string;
  metafieldNamespace: string;
  operatorShopName: string;
  operatorShopId: number;
  cartMaxItems: number;
};

export function useConfiguration() {
  const appMetafields = useAppMetafields({
    type: "shop",
    namespace: "mktp",
    key: "middleware_configuration",
  });

  const [config, setConfig] = useState<Configuration>({
    debugMode: false,
    middlewareUrl: "",
    middlewareToken: "",
    cartPropertiesPrefix: "mktp_",
    metafieldNamespace: "mktp",
    operatorShopName: "Operator",
    operatorShopId: 2000,
    cartMaxItems: 20,
  });

  const [configInit, setConfigInit] = useState(false);
  const [configurationError, setConfigurationError] = useState(false);

  useEffect(() => {
    const initConfiguration = async () => {
      try {
        const configuration = JSON.parse(
          String(appMetafields[0]?.metafield?.value || "{}")
        ) as Configuration;

        if (!configInit && configuration.debugMode !== undefined) {
          console.log("configuration (init)", configuration);
          if (
            configuration.middlewareUrl === "" ||
            configuration.middlewareToken === ""
          ) {
            setConfigurationError(true);
          } else {
            setConfig(configuration);
            setConfigInit(true);
          }
        }
      } catch (error) {
        console.error("Error fetching configuration:", error);
        setConfigurationError(true);
      }
    };

    initConfiguration();
  }, [appMetafields, configInit]);

  return { config, configInit, configurationError };
}

This hook is working fine in the first extension on which i implement it.

But when i try on another extension (same target) i got this error :

ExtensionUsageError: Error: Uncaught CheckoutUIExtensionError: You can only call this hook when running as a UI extension.


Even if this is obviously a UI Extension...

Here is some code on how the code is called :

import {
  OrderType,
  getCartAttributeKey,
} from "../../../app/components/Utils.ts";
import { useConfiguration } from "../../../app/hooks/useConfiguration.ts";
import { useState, useCallback, useEffect } from "react";
import {
  Loader,
  ShippingMethods,
  DiscountCode,
  ShippingProvidersIcons,
} from "./components";
import { getShippingMethods, getShopFromShippingMethods } from "./utils.js";
import {
  reactExtension,
  useApi,
  useBuyerJourneyIntercept,
  BlockStack,
  Banner,
  useCartLines,
  useApplyCartLinesChange,
  useApplyMetafieldsChange,
  useApplyAttributeChange,
  useCheckoutToken,
  useShippingAddress,
  useAttributeValues,
  useDiscountCodes,
} from "@shopify/ui-extensions-react/checkout";

export default reactExtension(
  "purchase.checkout.shipping-option-list.render-before",
  () => <Extension />
);

const Extension = () => {
  const { i18n, localization } = useApi();
  const translate = i18n.translate;
  const checkoutToken = useCheckoutToken();
  const cartLines = useCartLines();
  const shippingAddress = useShippingAddress();
  const applyMetafieldsChange = useApplyMetafieldsChange();
  const applyCartLinesChange = useApplyCartLinesChange();
  const applyAttributeChange = useApplyAttributeChange();
  const discountCodes = useDiscountCodes();

  // HERE IS THE USAGE OF THE USECONFIGURATION HOOK
  const { config } = useConfiguration();
  console.log("configuration (re-use from 2nd extension)", config);
  const middlewareUrl = config.middlewareUrl;
  const token = config.middlewareToken;
  const cartPropertiesPrefix = config.cartPropertiesPrefix;
  const metafieldNamespaces = config.metafieldNamespace;

  (....)

I try to implement my hook only on this extension (removing it from the working one) and same error.
I don't know what i'm missing because the 2 extensions are not so different.

Any ideas ? Or suggestions on how to share configurations across several UI extension ?
Thanks for your help.

PS : Please not that i'm not an experienced react dev 😉

Replies 0 (0)