Graphql Storefront API always loading no data no error

Solved
nandezer
Tourist
12 2 8

I am building a nodejs react Shopify app.

 

On my index.js I have a mutation that works fine when I give a specific hardcode id. Now I'm trying to get that product id by query in order to pass it to the mutation, but that doesn't return any data.

Here is my code:

 

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

import {
  Banner,
  DisplayText,
  EmptyState,
  Frame,
  Heading,
  Layout,
  Page,
  PageActions,
  TextField,
  TextStyle,
  Toast,
  Card,
  ResourceList,
} from '@shopify/polaris';
import { Context } from '@shopify/app-bridge-react';
import gql from 'graphql-tag';
import React from 'react';
import { Query } from 'react-apollo';
import { Mutation } from 'react-apollo';

const UPDATE_PRICE = gql`
  mutation productUpdate($input: ProductInput!) {
	  productUpdate(input: $input) {
		product {
		  id
		}
		userErrors {
		  field
		  message
		}
	  }
	}
`;
const GET_PRODUCT = gql`
  query products {
   productByHandle(handle:"product-handle") {
    id
  }
}`;
var productId = "";

class Index extends React.Component {
  state = {
	showToast: false,
  };
	
  render() {
    return (
		<Frame>
			<Page>
				<Layout>
//This is not returning data
					<Query query={GET_PRODUCT }>
						{({data, loading, error}) => {
							if(error){
								console.log("Error: " + error);
							}
 //This is always true!
							if(loading){
								console.log("Loading...");
							}
							if (data){
								console.log(data.productByHandle.id);
							}
							return (
								<div>	
								</div>
							);
						}}
					</Query>
					
					//This works fine
					<Mutation
					mutation={UPDATE_PRICE}>
					{(handleSubmit, {error, data}) => {
						const showError = error && (
							<Banner status="critical">{error.message}</Banner>
						);
						const showToast = data && (
							<Toast
								content="Sucessfully updated"
								onDismiss={() => this.setState({ showToast: false })}
							/>
						);
						return (
							  <Page>
								<Layout>
								{showToast}
								  <Layout.Section>
									{showError}
								  </Layout.Section>
								  <PageActions
									primaryAction={[
									  {
										content: 'Turn on',
										onAction: () => {
											const productInput = {
											  id: "gid://shopify/Product/6604579405978",
											  status: "ACTIVE",
											};
											;
											handleSubmit({
												variables: { input: productInput },
											});
										}
									  }
									]}
								  />
								  <PageActions
									primaryAction={[
									  {
										content: 'Turn off',
										onAction: () => {
											const productInput = {
											   id: "gid://shopify/Product/6604579405978",
											   status: "DRAFT",
											};
											
											handleSubmit({
												variables: { input: productInput },
											});
										}
									  }
									]}
								  />
								</Layout>
							  </Page>
						);
					}}
					</Mutation>									
			</Page>
		</Frame>
	);
  }
}
export default Index;

 

This is my _app.js

import ApolloClient from "apollo-boost";
import { ApolloProvider } from "react-apollo";
import App from "next/app";
import { AppProvider } from "@shopify/polaris";
import { Provider, useAppBridge } from "@shopify/app-bridge-react";
import { authenticatedFetch } from "@shopify/app-bridge-utils";
import "@shopify/polaris/dist/styles.css";
import translations from "@shopify/polaris/locales/en.json";

function MyProvider(props) {
  const app = useAppBridge();

  const client = new ApolloClient({
    fetch: authenticatedFetch(app),
    fetchOptions: {
      credentials: "include",
    },
  });

  const Component = props.Component;

  return (
    <ApolloProvider client={client}>
      <Component {...props} />
    </ApolloProvider>
  );
}

class MyApp extends App {
  render() {
    const { Component, pageProps, shopOrigin } = this.props;
    return (
      <AppProvider i18n={translations}>
        <Provider
          config={{
            apiKey: API_KEY,
            shopOrigin: shopOrigin,
            forceRedirect: true,
          }}
        >
          <MyProvider Component={Component} {...pageProps} />
        </Provider>
      </AppProvider>
    );
  }
}

MyApp.getInitialProps = async ({ ctx }) => {
  return {
    shopOrigin: ctx.query.shop,
  };
};

export default MyApp;

 

Anyone has an idea why data is always undefined?

Accepted Solution (1)

Accepted Solutions
nandezer
Tourist
12 2 8

This is an accepted solution.

Hey, 

I did. I went through several changes on my index.js already so can't exactly recall what I changed, but here is my index.js & _app.js and hopefully helps you you figure it out.

 

Index.js

import { TitleBar } from "@shopify/app-bridge-react";

import {
  Banner,
  EmptyState,
  Frame,
  Heading,
  Layout,
  Page,
  PageActions,
  TextField,
  TextStyle,
  Toast,
  Card,
  ResourceList,
} from "@shopify/polaris";
import { Context } from "@shopify/app-bridge-react";
import gql from "graphql-tag";
import React from "react";
import { Query } from "react-apollo";
import { Mutation } from "react-apollo";
import { useQuery } from "@apollo/react-hooks";

const UPDATE_PRICE = gql`
  mutation productUpdate($input: ProductInput!) {
    productUpdate(input: $input) {
      product {
        id
      }
      userErrors {
        field
        message
      }
    }
  }
`;
const GET_PRODUCT = gql`
  query products {
    productByHandle(handle: "your-handle") {
      id
    }
  }
`;

var productID = "";

class Index extends React.Component {
  state = {
    showToast: false,
  };

  render() {
    return (
      <Frame>
        <Page >
          <Layout>
            <Query query={GET_PRODUCT}>
              {({ data, loading, error }) => {
                if (loading) {
                  console.log("Loading....");
                  return <div></div>;
                }
                if (error) {
                  console.log("Error: ", error.message);
                  return <div></div>;
                }
                console.log("data: ", data.productByHandle.id);
                productID = data.productByHandle.id;
                return <div></div>;
              }}
            </Query>
            <Mutation mutation={UPDATE_PRICE}>
              {(handleSubmit, { error, data }) => {
                const showError = error && (
                  <Banner status="critical">{error.message}</Banner>
                );
                const showToast = data && (
                  <Toast
                    content="Sucessfully updated"
                    onDismiss={() => this.setState({ showToast: false })}
                  />
                );
                return (
                  <Page>
                    <Layout>
                      {showToast}
                      <Layout.Section>{showError}</Layout.Section>
                      <PageActions
                        primaryAction={[
                          {
                            content: "Turn on",
                            onAction: () => {
                              console.log("turn on");
                              const productInput = {
                                id: productID,
                                status: "ACTIVE",
                              };
                              handleSubmit({
                                variables: { input: productInput },
                              });
                            },
                          },
                        ]}
                      />
                      <PageActions
                        primaryAction={[
                          {
                            content: "Turn off",
                            onAction: () => {
                              console.log("turn off");
                              const productInput = {
                                id: productID,
                                status: "DRAFT",
                              };

                              handleSubmit({
                                variables: { input: productInput },
                              });
                            },
                          },
                        ]}
                      />
                    </Layout>
                  </Page>
                );
              }}
            </Mutation>
          </Layout>
        </Page>
      </Frame>
    );
  }
}
export default Index;

 

_app.js

import ApolloClient from "apollo-boost";
import { ApolloProvider } from "react-apollo";
import App from "next/app";
import { AppProvider } from "@shopify/polaris";
import { Provider, useAppBridge } from "@shopify/app-bridge-react";
import { authenticatedFetch } from "@shopify/app-bridge-utils";
import { Redirect } from "@shopify/app-bridge/actions";
import "@shopify/polaris/dist/styles.css";
import translations from "@shopify/polaris/locales/en.json";

function userLoggedInFetch(app) {
  const fetchFunction = authenticatedFetch(app);
  return async (uri, options) => {
    const response = await fetchFunction(uri, options);

    if (response.headers.get("X-Shopify-API-Request-Failure-Reauthorize") === "1") {
      const authUrlHeader = response.headers.get("X-Shopify-API-Request-Failure-Reauthorize-Url");

      const redirect = Redirect.create(app);
      redirect.dispatch(Redirect.Action.APP, authUrlHeader || `/auth`);
      return null;
    }

    return response;
  };
}

function MyProvider(props) {
  const app = useAppBridge();

  const client = new ApolloClient({
    fetch: userLoggedInFetch(app),
    fetchOptions: {
      credentials: "include",
    },
  });

  const Component = props.Component;

  return (
    <ApolloProvider client={client}>
      <Component {...props} />
    </ApolloProvider>
  );
}

class MyApp extends App {
  render() {
    const { Component, pageProps, shopOrigin } = this.props;
    return (
      <AppProvider i18n={translations}>
        <Provider
          config={{
            apiKey: API_KEY,
            shopOrigin: shopOrigin,
            forceRedirect: true,
          }}
        >
          <MyProvider Component={Component} {...pageProps} />
        </Provider>
      </AppProvider>
    );
  }
}

MyApp.getInitialProps = async ({ ctx }) => {
  return {
    shopOrigin: ctx.query.shop,
  };
};

export default MyApp;

 

Good luck!

View solution in original post

Replies 3 (3)
FrankLi
Tourist
11 2 1

Hi, do you solve this problem ? 

nandezer
Tourist
12 2 8

This is an accepted solution.

Hey, 

I did. I went through several changes on my index.js already so can't exactly recall what I changed, but here is my index.js & _app.js and hopefully helps you you figure it out.

 

Index.js

import { TitleBar } from "@shopify/app-bridge-react";

import {
  Banner,
  EmptyState,
  Frame,
  Heading,
  Layout,
  Page,
  PageActions,
  TextField,
  TextStyle,
  Toast,
  Card,
  ResourceList,
} from "@shopify/polaris";
import { Context } from "@shopify/app-bridge-react";
import gql from "graphql-tag";
import React from "react";
import { Query } from "react-apollo";
import { Mutation } from "react-apollo";
import { useQuery } from "@apollo/react-hooks";

const UPDATE_PRICE = gql`
  mutation productUpdate($input: ProductInput!) {
    productUpdate(input: $input) {
      product {
        id
      }
      userErrors {
        field
        message
      }
    }
  }
`;
const GET_PRODUCT = gql`
  query products {
    productByHandle(handle: "your-handle") {
      id
    }
  }
`;

var productID = "";

class Index extends React.Component {
  state = {
    showToast: false,
  };

  render() {
    return (
      <Frame>
        <Page >
          <Layout>
            <Query query={GET_PRODUCT}>
              {({ data, loading, error }) => {
                if (loading) {
                  console.log("Loading....");
                  return <div></div>;
                }
                if (error) {
                  console.log("Error: ", error.message);
                  return <div></div>;
                }
                console.log("data: ", data.productByHandle.id);
                productID = data.productByHandle.id;
                return <div></div>;
              }}
            </Query>
            <Mutation mutation={UPDATE_PRICE}>
              {(handleSubmit, { error, data }) => {
                const showError = error && (
                  <Banner status="critical">{error.message}</Banner>
                );
                const showToast = data && (
                  <Toast
                    content="Sucessfully updated"
                    onDismiss={() => this.setState({ showToast: false })}
                  />
                );
                return (
                  <Page>
                    <Layout>
                      {showToast}
                      <Layout.Section>{showError}</Layout.Section>
                      <PageActions
                        primaryAction={[
                          {
                            content: "Turn on",
                            onAction: () => {
                              console.log("turn on");
                              const productInput = {
                                id: productID,
                                status: "ACTIVE",
                              };
                              handleSubmit({
                                variables: { input: productInput },
                              });
                            },
                          },
                        ]}
                      />
                      <PageActions
                        primaryAction={[
                          {
                            content: "Turn off",
                            onAction: () => {
                              console.log("turn off");
                              const productInput = {
                                id: productID,
                                status: "DRAFT",
                              };

                              handleSubmit({
                                variables: { input: productInput },
                              });
                            },
                          },
                        ]}
                      />
                    </Layout>
                  </Page>
                );
              }}
            </Mutation>
          </Layout>
        </Page>
      </Frame>
    );
  }
}
export default Index;

 

_app.js

import ApolloClient from "apollo-boost";
import { ApolloProvider } from "react-apollo";
import App from "next/app";
import { AppProvider } from "@shopify/polaris";
import { Provider, useAppBridge } from "@shopify/app-bridge-react";
import { authenticatedFetch } from "@shopify/app-bridge-utils";
import { Redirect } from "@shopify/app-bridge/actions";
import "@shopify/polaris/dist/styles.css";
import translations from "@shopify/polaris/locales/en.json";

function userLoggedInFetch(app) {
  const fetchFunction = authenticatedFetch(app);
  return async (uri, options) => {
    const response = await fetchFunction(uri, options);

    if (response.headers.get("X-Shopify-API-Request-Failure-Reauthorize") === "1") {
      const authUrlHeader = response.headers.get("X-Shopify-API-Request-Failure-Reauthorize-Url");

      const redirect = Redirect.create(app);
      redirect.dispatch(Redirect.Action.APP, authUrlHeader || `/auth`);
      return null;
    }

    return response;
  };
}

function MyProvider(props) {
  const app = useAppBridge();

  const client = new ApolloClient({
    fetch: userLoggedInFetch(app),
    fetchOptions: {
      credentials: "include",
    },
  });

  const Component = props.Component;

  return (
    <ApolloProvider client={client}>
      <Component {...props} />
    </ApolloProvider>
  );
}

class MyApp extends App {
  render() {
    const { Component, pageProps, shopOrigin } = this.props;
    return (
      <AppProvider i18n={translations}>
        <Provider
          config={{
            apiKey: API_KEY,
            shopOrigin: shopOrigin,
            forceRedirect: true,
          }}
        >
          <MyProvider Component={Component} {...pageProps} />
        </Provider>
      </AppProvider>
    );
  }
}

MyApp.getInitialProps = async ({ ctx }) => {
  return {
    shopOrigin: ctx.query.shop,
  };
};

export default MyApp;

 

Good luck!

FrankLi
Tourist
11 2 1

Thanks a lot!!!