// package/shared
import { AdminBlock } from "@shopify/ui-extensions-react/admin";
import TargetProvider from "./providers/TargetProvider";
// this BlockApp can be used in different contexts, and the target prop allows the implementations to switch APIs, queries, mutations, etc based on the target
export function BlockApp({ target }) {
return (
<AdminBlock title="My Extension">
<TargetProvider target={target}>
{/* rest of my components code */}
</TargetProvider>
</AdminBlock>
)
}
Then I had a useTarget hook to leverage the target dynamically across these different admin extensions.
But it looks like that no longer works. Probably not a supported use case, but it was really nice to be able to provide similar functionality across different extensions without having to manage copy and pasted code.
The only solution I’ve found so far is to copy and paste the shared code directly into the individual extensions now.
Definitely a nice setup. More scalable for sure. I’d expect it to be possible, so hopefully it’s a simple fix.
For now, I’ll try to reproduce it on my side. If you have a minimal reproducible version of the issue on a repo, it’d make it easier for me to debug your use-case.
Thanks @Olavo - it would be really nice if workspace packages could be supported
Here’s a few more snippets for context.
// TargetProvider.jsx
import { createContext } from "react";
// Create a context for the check state
export const TargetContext = createContext(null);
// Create a provider component
export default ({ target, children }) => {
console.log(`TARGET :: ${target}`);
return (
<TargetContext.Provider
value={{
target,
}}
>
{children}
</TargetContext.Provider>
);
};
Example implementation where the bug occurs:
import { useApi, Text } from "@shopify/ui-extensions-react/admin";
import { useTarget } from "../hooks/useTarget";
export default function SharedComponent() {
const { data } = useApi(target);
return (
<Text>{ JSON.stringify(data) }</Text>
)
}
// results in thrown error, even when `TARGET: ${target name}` is logged to console
// AdminUIExtensionError: No extension api found.
You can skip the provider implementation and try to use useApi(TARGET) in the root component of the shared package and the error still happens.
So it’s just a general incompatibility at this time I think.
// package/shared
import { AdminBlock, useApi } from "@shopify/ui-extensions-react/admin";
import TargetProvider from "./providers/TargetProvider";
// this BlockApp can be used in different contexts, and the target prop allows the implementations to switch APIs, queries, mutations, etc based on the target
export function BlockApp({ target }) {
// results in the error
const { data } = useApi(target);
// also hardcoding the target results in the same error:
const { data } = useApi("admin.order-details.block.render");
return (
<AdminBlock title="My Extension">
{/* rest of my components code */}
</AdminBlock>
)
}
So it looks like shared packages just aren’t feasible anymore from what I can tell.
I’m not sure how you’ve set this up on your repo, but, seeing that I didn’t have the issue, what comes to mind here it that maybe you have multiple versions of react active. Are you building the shared packages as a library? Does it have have react as a dependency?
Here’s my attempt to reproduce the issue. Its seems to be working, though it’s a simple implementation. If you want to try it out, clone the repo, run:
pnpm i && pnpm shopify dev --reset
You should see the extensions on the product and order details pages.
Nice, thanks for the repo. You took a slightly different approach.
I’ve explicitly defined a package.json in the packages/shared so that way react is pinned to the same version as the extensions.
And that means it’s possible to accidentally have different @shopify/ui-extensions-react and @shopify/ui-extensions versions from your consuming extensions by accident.
Switching the shared package’s ui-extension to the same version (2024-07) as the order/customer extension packages seems to have fixed the issue.
Ah! Yea, that’s likely the case then. Monorepos are tricky with this kinda thing and it’s easy to fall into this situation. You can probably set those packages as peer dependencies on the shared package and make sure they’re treated as external by the bundler.
No worries! Glad it worked out.
It’s great to explore these types of patterns. Don’t get discouraged and keep on building! We’re here if you need help.