3

Background:

I am trying to create some links in my embedded Shopify app.

I understand that I cannot use the simple <a> tag due to the fact that Shopify embedded apps are rendered as iframes.

I made some headway with this tutorial, but I am stuck: https://theunlikelydeveloper.com/shopify-app-bridge-react-router/

What I am trying to do:

I have 3 pages (index.js, dashboard.js, and support.js). I would like to allow the user to navigate from one page to another (with links and/or buttons).

My Code:

By following the tutorial above, I've gotten this far:

// index.js
import { Page, Frame } from "@shopify/polaris";

const Index = () => {
  return (
    <Page>
      <Frame>
        {/* LINK TO DASHBOARD PAGE*/}
        {/* LINK TO SUPPORT PAGE */}
      </Frame>
    </Page>
  );
};

export default Index;
// routes.js
import React from "react";
import { Switch, Route, withRouter } from "react-router";
import { ClientRouter, RoutePropagator } from "@shopify/app-bridge-react";

function Routes(props) {
  const { history, location } = props;

  return (
    <>
      <ClientRouter history={history} />
      <RoutePropagator location={location} />
      <Switch>
        <Route path="/dashboard">
          <Dashboard />
        </Route>
        <Route path="/support">
          <Support />
        </Route>
        <Route path="/">
          <Home />
        </Route>
      </Switch>
    </>
  );
}

export default withRouter(Routes);
// link.js
import React from "react";
import { Link as ReactRouterLink } from "react-router";

const IS_EXTERNAL_LINK_REGEX = /^(?:[a-z][a-z\d+.-]*:|\/\/)/;

function Link({ children, url = "", external, ref, ...rest }) {
  if (external || IS_EXTERNAL_LINK_REGEX.test(url)) {
    rest.target = "_blank";
    rest.rel = "noopener noreferrer";
    return (
      <a href={url} {...rest}>
        {children}
      </a>
    );
  }

  return (
    <ReactRouterLink to={url} {...rest}>
      {children}
    </ReactRouterLink>
  );
}

export default Link;

Additional:

I believe I'm supposed to implement the following code somewhere, but I don't see how it fits into the picture of navigating between pages with a link or button.

<AppProvider linkComponent={Link}>
  {/* App content including your <Route> components */}
</AppProvider>
Brandon
  • 155
  • 10
  • Seems your `Link` would just replace all the `Link` components previously imported from `react-router`. – Drew Reese Apr 08 '21 at 01:18
  • Thanks, @DrewReese - does that mean that I need to include the `` somewhere? If so, what would I need to put inside it? – Brandon Apr 08 '21 at 01:26
  • Also, I tried simply adding something like `Go to Dashboard` into index.js, but it didn't work. Any ideas? – Brandon Apr 08 '21 at 01:32
  • Did you import `AppProvider` from `'@shopify/polaris'` and wrap your code in your router with it? Also, do you have a `Router` component somewhere as well? – Drew Reese Apr 08 '21 at 02:00
  • So I'm assuming that I need to edit my index.js file and 1) import AppProvider, 2) include that `linkComponent` code from above, 3) create a router of some sort. I have a ClientRouter.js component that I got from [Shopify Docs](https://shopify.dev/tutorials/build-a-shopify-app-with-node-and-react/build-your-user-interface-with-polaris#add-the-client-router). Am I on the right track here? – Brandon Apr 08 '21 at 02:22
  • Possibly. It looks like that `ClientRouter` component just needs to sit in the main router and consume a `router` prop it receives from `withRouter` HOC. – Drew Reese Apr 09 '21 at 17:57
  • @Brandon Did you ever get this working? – InquisitiveTom Dec 07 '21 at 22:18

1 Answers1

2

At this time of building embedded app you can make client-side navigation using app-bridge utilities as referred to in this answer

You just need to edit _app file and consider making client-side navigation from your components(can't use a normal Link)


import {useEffect} from 'react';
import Router, { useRouter } from "next/router";
import { RoutePropagator as ShopifyRoutePropagator } from "@shopify/app-bridge-react";


function RoutePropagator () {
  const router = useRouter();
  const { route } = router;
  const app= useAppBridge();

  useEffect(() => {
    app.subscribe(Redirect.Action.APP, ({ path }) => {
      Router.push(path);
    });
  }, []);

  return app && route ? (
    <ShopifyRoutePropagator location={route} />
    ) : null;
}

Then use this component in your _app file

_app.tsx


class MyApp extends App {

  render() {

    const { Component, pageProps, host } = this.props as any;

    return (
      <PolarisProvider i18n={translations}>

        <ShopifyBridgeProvider
          config={{
            apiKey: API_KEY,
              host,
              forceRedirect: true,
          }}
        >
          <RoutePropagator />
          <ApolloClientProvider Component={Component} {...pageProps} />

        </ShopifyBridgeProvider>

      </PolarisProvider>
    );
  }
}

Now you've subscribed for routing events in _app file, we just require to make client-side navigation right in your pages

import {useAppBridge} from '@shopify/app-bridge-react';
import  { Redirect } from '@shopify/app-bridge/actions';

function IndexPage(props) {

  const app = useAppBridge();

  return (
    <>
      <div>{'you are in main page'}</div>

      <div onClick={() => {
        app.dispatch(Redirect.toApp({
          path: '/dashboard'
        }));
      }}>
        {'to dashboard'}
      </div>
    </>
  );

}

And for going back to the main page / route, I've found that it trigger an oauth again if not provided with the shop name, so we will use the shop query params for that

 <div onClick={() => {
      app.dispatch(Redirect.toApp({
        path: '/?shop=<shop-name>.myshopify.com'
      }));
    }}>
      {'to main'}
 </div>
Louay Al-osh
  • 3,177
  • 15
  • 28