How do I dispatch actions with the new shopify-app-cli application?

I’ve created an embedded react application using the new shopify-app-cli . I’m trying to dispatch a Redirect action but when I attempt to get the appbridge context it comes back undefined.

I’ve created another project by following the React, Node JS tutorial - and it contains the code below to get the context. This works perfectly within the tutorial project but when used in the CLI created project the appbridge context is undefined.

static contextTypes = {
 polaris: PropTypes.object,
};

redirectToProduct = () => {
 const redirect = Redirect.create(this.context.polaris.appBridge);
 redirect.dispatch(
  Redirect.Action.APP,
  '/edit'
 );
};

The _app file is different in the new CLI created application. It contains a Provider imported from @shopify/app-bridge-react which takes in a config object containing the API_KEY and shopOrigin. Is the appbridge context accessed through this Provider meaning it’s unnessesary to use createApp from shopify/app-bridge as in the code below taken from the Sopify Help Center?

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

const app = createApp({
  apiKey: '12345',
  shopOrigin: shopOrigin,
});

const redirect = Redirect.create(app);

Is there any documentation of @shopify/app-bridge-react that I can refer to as I’m sure I’m missing something obvious?

I’m also using the Loading and Titlebar components from @shopify/app-bridge-react which work great but I’m unsure how to dispatch an action or subscribe to an action when I can’t get the appbridge context. I can see via Redux Dev Tools that the START action is automatically dispatched when the Loading component is mounted but how can I manually dispatch the actions START and STOP? As I stated earlier, I can dispatch actions within the framework created within the tutorial project using this.context.polaris.appBridge as the app context but this doesn’t work in the CLI created project.

Hi there,

Accessing the app instance from the Provider from @shopify/app-bridge-react is slightly different - documentation is coming very soon.

Can you try the following code?

import {AppBridgeContext} from '@shopify/app-bridge-react/context';

class MyComponent extends React.Component {
  static contextType = AppBridgeContext;

  redirectToProduct = () => {
    // The app instance is accessible through the context
    const redirect = Redirect.create(this.context);
    redirect.dispatch(
     Redirect.Action.APP,
     '/edit'
    );
   };
}

Hi Trish

Thanks for a lot for the help. Unfortunately, I’d already tried this over the weekend after finding the AppBridgeContext exported in context.js in app-bridge-react, but the context is always null.

I’ve created a simple index.js page to demonstrate and console logged this.context. I haven’t amended the _app.js file. Interestingly, pageProps in _app.js is undefined - what is this for and should it have a value?

// index.js
import React, { Component } from "react";
import { Page, Heading, Button } from "@shopify/polaris";
import { AppBridgeContext } from "@shopify/app-bridge-react/context";
import { Redirect } from '@shopify/app-bridge/actions';

class MyComponent extends Component {
  static contextType = AppBridgeContext;

  redirectToProduct = () => {
    console.log('btn clicked',this.context); // returns null
    // The app instance is accessible through the context
    const redirect = Redirect.create(this.context);
    redirect.dispatch(Redirect.Action.APP, "/edit");
  };

  render() {
    console.log('render',this.context); // returns null
    return (
      <Page>
        <Heading>Shopify app with Node and React</Heading>
        <Button onClick={this.redirectToProduct}>Edit</Button>
      </Page>
    );
  }
}
export default MyComponent;
// _app.js

import ApolloClient from "apollo-boost";
import { ApolloProvider } from "react-apollo";
import App, { Container } from "next/app";
import { AppProvider } from "@shopify/polaris";
import { Provider } from "@shopify/app-bridge-react";
import "@shopify/polaris/styles.css";

const client = new ApolloClient({
  fetchOptions: {
    credentials: "include"
  }
});
class MyApp extends App {
  static async getInitialProps(server) {
    const shopOrigin = server.ctx.query.shop;
    return { shopOrigin };
  }
  render() {
    const { Component, pageProps, shopOrigin } = this.props;
    return (
      <Container>
        <AppProvider>
          <Provider
            config={{
              apiKey: API_KEY,
              shopOrigin: shopOrigin,
              forceRedirect: true
            }}
          >
            <ApolloProvider client={client}>
              <Component {...pageProps} shopOrigin={shopOrigin} />
            </ApolloProvider>
          </Provider>
        </AppProvider>
      </Container>
    );
  }
}

export default MyApp;

This is my package.json file.

{
  "name": "shopify-node-app",
  "version": "1.0.0",
  "description": "Shopify's node app for CLI tool",
  "scripts": {
    "test": "jest",
    "dev": "NODE_ENV=development nodemon ./server/index.js --watch ./server/index.js",
    "build": "next build",
    "start": "NODE_ENV=production node ./server/index.js",
    "generate-page": "node scripts/index.js generate-page",
    "generate-recurring-billing": "node scripts/index.js generate-recurring-billing",
    "generate-one-time-billing": "node scripts/index.js generate-one-time-billing",
    "generate-webhook": "node scripts/index.js generate-webhook"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/Shopify/shopify-node-app.git"
  },
  "author": "Shopify Inc.",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/shopify/shopify-node-app/issues"
  },
  "jest": {
    "clearMocks": true
  },
  "dependencies": {
    "@babel/core": "7.3.4",
    "@babel/polyfill": "^7.4.3",
    "@babel/register": "^7.4.0",
    "@shopify/app-bridge-react": "^1.5.3",
    "@shopify/koa-shopify-auth": "^3.1.28",
    "@shopify/koa-shopify-graphql-proxy": "^3.1.1",
    "@shopify/koa-shopify-webhooks": "^1.1.8",
    "@shopify/polaris": "^3.16.0",
    "@zeit/next-css": "^1.0.1",
    "apollo-boost": "^0.4.3",
    "babel-preset-env": "^1.7.0",
    "dotenv": "^7.0.0",
    "graphql": "^14.2.1",
    "isomorphic-fetch": "^2.1.1",
    "koa": "^2.7.0",
    "koa-router": "^7.4.0",
    "koa-session": "^5.10.1",
    "lodash.get": "^4.4.2",
    "next": "^8.1.0",
    "next-env": "^1.1.0",
    "react": "^16.8.6",
    "react-apollo": "^2.5.6",
    "react-dom": "^16.8.6"
  },
  "devDependencies": {
    "@babel/plugin-transform-runtime": "^7.4.3",
    "@babel/preset-stage-3": "^7.0.0",
    "babel-jest": "24.1.0",
    "babel-register": "^6.26.0",
    "enzyme": "3.4.3",
    "enzyme-adapter-react-16": "1.2.0",
    "husky": "^2.2.0",
    "jest": "24.1.0",
    "lint-staged": "^8.1.6",
    "nodemon": "^1.18.11",
    "prettier": "1.17.0",
    "react-addons-test-utils": "15.6.2",
    "react-test-renderer": "16.4.2"
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.{js,css,json,md}": [
      "prettier --write",
      "git add"
    ]
  }
}

Hey clucs123,

Can you paste the code from your index.js file? I just create my own test app and can confirm that the app instance can be accessed through the context. I did not make any changes to the _app.js file.

Here’s the full content of my index.js file:

import { Heading, Page } from "@shopify/polaris";
import {AppBridgeContext} from '@shopify/app-bridge-react/context';
import {Redirect} from '@shopify/app-bridge/actions';

class MyComponent extends React.Component {
  static contextType = AppBridgeContext;

  redirectToProduct = () => {
    // The app instance is accessible through the context
    const redirect = Redirect.create(this.context);
    redirect.dispatch(
     Redirect.Action.APP,
     '/edit'
    );
  };

  render() {
    console.log(this.context); // Outputs the app instance
    return 'hi';
  }

}

const Index = () => (
  <Page>
    <Heading>Shopify app with Node and React ?</Heading>
    <MyComponent/>
  </Page>
);

Hi Trish,

This is my full index.js code. I tried your code but sadly I still get null back. I included a screenshot of the browser console showing what happens when the button is clicked.

Thanks again for the help,

Paul

import React, { Component } from "react";
import { Page, Heading, Button } from "@shopify/polaris";
import { AppBridgeContext } from "@shopify/app-bridge-react/context";
import { Redirect } from '@shopify/app-bridge/actions';

class MyComponent extends Component {
  static contextType = AppBridgeContext;

  redirectToProduct = () => {
    console.log('btn clicked',this.context); // return null
    // The app instance is accessible through the context
    const redirect = Redirect.create(this.context);
    redirect.dispatch(Redirect.Action.APP, "/edit");
  };

  render() {
    console.log('render',this.context); // returns null
    return (
      <Page>
        <Heading>Shopify app with Node and React �</Heading>
        <Button onClick={this.redirectToProduct}>Edit</Button>
      </Page>
    );
  }
}

const Index = () => (
  <Page>
    <MyComponent/>
  </Page>
);

export default Index;

Hi Paul,

I just tried your code and the context is showing up for me.

cli-app-redirect-test.gif

Could there be something wrong with the configs passed to the Provider? Can you try to hardcode the shopOrigin with your test shop’s domain (Ex. myshopname.myshopify.com) and see if that works?

Hi Trish,

Well, it’s good to know that my code does actually work. I agree that it must be a config issue. I’ve tried hard-coding the config values as you suggested but it still doesn’t work. I’ve also checked the API_KEY and shopOrigin variables are populated correctly by console logging them.

I’ve noticed if I change the API_KEY or shopOrigin to incorrect values the loading START action isn’t dispatched on one of my other pages which contains a Loading component. So it seems the credentials are definitely doing something. The screenshot below shows the loading START action has been dispatched - in this case the credentials are set to their correct values.

Is my _app.js file correct and all package dependencies up to date?

Thanks

Paul

Just to be on the safe side I’ve created a new app in Shopify and a new project from scratch using the CLI and added the simple test component above. The app runs in the test store but again the context is always null.

// index.js
import { Heading, Page } from "@shopify/polaris";
import {AppBridgeContext} from '@shopify/app-bridge-react/context';
import {Redirect} from '@shopify/app-bridge/actions';

class MyComponent extends React.Component {
  static contextType = AppBridgeContext;

  redirectToProduct = () => {
    // The app instance is accessible through the context
    const redirect = Redirect.create(this.context);
    redirect.dispatch(
     Redirect.Action.APP,
     '/edit'
    );
  };

  render() {
    console.log(this.context); // Outputs the app instance
    return 'hi';
  }

}

const Index = () => (
  <Page>
    <Heading>Shopify app with Node and React ?</Heading>
    <MyComponent/>
  </Page>
);
// _app.js
import ApolloClient from "apollo-boost";
import { ApolloProvider } from "react-apollo";
import App, { Container } from "next/app";
import { AppProvider } from "@shopify/polaris";
import { Provider } from "@shopify/app-bridge-react";
import "@shopify/polaris/styles.css";

const client = new ApolloClient({
  fetchOptions: {
    credentials: "include"
  }
});
class MyApp extends App {
  static async getInitialProps(server) {
    const shopOrigin = server.ctx.query.shop;
    return { shopOrigin };
  }
  render() {
    const { Component, pageProps, shopOrigin } = this.props;
    return (
      <Container>
        <AppProvider>
          <Provider
            config={{
              apiKey: API_KEY,
              shopOrigin: shopOrigin,
              forceRedirect: true
            }}
          >
            <ApolloProvider client={client}>
              <Component {...pageProps}  />
            </ApolloProvider>
          </Provider>
        </AppProvider>
      </Container>
    );
  }
}

export default MyApp;

Try running your app inside Shopify admin.

App Bridge is the connection between your app and Shopify admin and doesn’t really work without an Admin Frame.

1 Like

To echo what Michelle said, this app needs to be rendered within the admin for any context to exist. If you’re hitting it from your ngrok URL you won’t have any luck accessing App Bridge features.

You can install the app on your development store with this url:

HTTPS://your-ngrok-url.io/auth?shop=your-development-store.myshopify.com

Hi Katie

The app has always been running inside in the Shopify Admin. I’ve console logged the context and as you can see it is null.

So sorry about that @clucs123 ! I’m wondering if you could put your code up on github? I’ll try running the whole app to see whats going on.

Hi Katie

https://github.com/clucs123/shopify-react-test

No problem. I haven’t amended this project since it was created in the shopify-app-cli, apart from the index.js file and my .env config file.

The .env file obviously isn’t on GitHub for security reasons but this is its structure. I’ve created a couple of other embedded apps now and I haven’t had any other issues. All the components seem to work as expected. The Loading component automatically dispatches a START action when mounted for example. I just need to access the context so I can dispatch actions when I want. Thanks for your help, Katie. I’ll be so happy when this is resolved!

I don’t know if this matters at all but I created the project in Windows 10 using the Ubuntu VM as outlined in the shopify-app-cli readme … https://github.com/Shopify/shopify-app-cli.

SHOPIFY_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxx
SHOPIFY_API_SECRET_KEY=xxxxxxxxxxxxxxxxxxxxxxxx
HOST=https://xxxxxxxxx.ngrok.io
SHOP=pauls-dev-store.myshopify.com
SCOPES=write_products,write_customers,write_draft_orders

no problem @clucs123 I’ll take a look today and let you know what I find, it just might take a bit.

1 Like

hey @clucs123 with some investigation with @Trish_Ta we’ve figured out the issue!

can you add @babel/preset-env? the package we were using (babel-preset-env) has been moved into that! It solved the issue on both our ends

Hey Katie,

Can I just confirm, do I install the @babel/preset-env package as a dev dependency as per install instructions at https://babeljs.io/docs/en/babel-preset-env ?

Also, do I have to uninstall the babel-preset-env package from the regular dependencies?

Thanks

Paul

Hey Katie

I can see the context written back to the terminal in VSCode but it’s still null in the browser but at least we are getting somewhere :slight_smile:

The @babel/preset-env is installed as a regular dependency. If I install it as a dev dependency the context isn’t written back to the terminal. I’ve added my package.json below along with screenshots of the browser console and terminal window.

Thanks again,

Paul.

{
  "name": "shopify-node-app",
  "version": "1.0.0",
  "description": "Shopify's node app for CLI tool",
  "scripts": {
    "test": "jest",
    "dev": "NODE_ENV=development nodemon ./server/index.js --watch ./server/index.js",
    "build": "next build",
    "start": "NODE_ENV=production node ./server/index.js",
    "generate-page": "node scripts/index.js generate-page",
    "generate-recurring-billing": "node scripts/index.js generate-recurring-billing",
    "generate-one-time-billing": "node scripts/index.js generate-one-time-billing",
    "generate-webhook": "node scripts/index.js generate-webhook"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/Shopify/shopify-node-app.git"
  },
  "author": "Shopify Inc.",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/shopify/shopify-node-app/issues"
  },
  "jest": {
    "clearMocks": true
  },
  "dependencies": {
    "@babel/core": "7.3.4",
    "@babel/polyfill": "^7.4.3",
    "@babel/preset-env": "^7.5.0",
    "@babel/register": "^7.4.0",
    "@shopify/app-bridge-react": "^1.5.3",
    "@shopify/koa-shopify-auth": "^3.1.28",
    "@shopify/koa-shopify-graphql-proxy": "^3.1.1",
    "@shopify/koa-shopify-webhooks": "^1.1.8",
    "@shopify/polaris": "^3.16.0",
    "@zeit/next-css": "^1.0.1",
    "apollo-boost": "^0.4.3",
    "dotenv": "^7.0.0",
    "graphql": "^14.2.1",
    "isomorphic-fetch": "^2.1.1",
    "koa": "^2.7.0",
    "koa-router": "^7.4.0",
    "koa-session": "^5.10.1",
    "lodash.get": "^4.4.2",
    "next": "^8.1.0",
    "next-env": "^1.1.0",
    "react": "^16.8.6",
    "react-apollo": "^2.5.6",
    "react-dom": "^16.8.6"
  },
  "devDependencies": {
    "@babel/plugin-transform-runtime": "^7.4.3",
    "@babel/preset-stage-3": "^7.0.0",
    "babel-jest": "24.1.0",
    "babel-register": "^6.26.0",
    "enzyme": "3.4.3",
    "enzyme-adapter-react-16": "1.2.0",
    "husky": "^2.2.0",
    "jest": "24.1.0",
    "lint-staged": "^8.1.6",
    "nodemon": "^1.18.11",
    "prettier": "1.17.0",
    "react-addons-test-utils": "15.6.2",
    "react-test-renderer": "16.4.2"
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.{js,css,json,md}": [
      "prettier --write",
      "git add"
    ]
  }
}

Hi again,

Both Trish and I had success with the changes made. Can you try generating a fresh app from the CLI?

Or for those using Hooks:

import React, { useContext } from 'react';
import { AppBridgeContext } from '@shopify/app-bridge-react/context';
import { Redirect } from '@shopify/app-bridge/actions';

export default (props) => {
  const polaris = useContext(AppBridgeContext);

  const redirectToProduct = () => {
    const redirect = Redirect.create(polaris);
    redirect.dispatch(Redirect.Action.APP, '/edit');
  }
}
1 Like