Search code examples
javascriptreactjsasynchronousshopifypolaris

How can I prevent a Shopify Polaris React app from connecting to Shopify service too soon?


I'm new to react and I'm trying to build a basic Shopify app using React and the Polaris React suite.

TL:DR;

How can I prevent a React component from rendering until data has been fetched asynchronously from the server? OR What is the correct method to get a Polaris app to connect to the shopify service?

Full Explanation

The problem is the render function should add the store domain to an attribute on the <AppProvider/> element. Ie. <AppProvider shopOrigin="https://user-store.myshopify.com"/> However, the domain differs depending on the store using the app.

Given that React is rendered on the client side, I need to send a request to the server to retrieve the current store domain. But this happens after the app has rendered:

render() {

    return (
      <AppProvider
        shopOrigin={this.state.shop} {/* Null when first rendered  */}
        debug={true}
      > ... </AppProvider>

This results in the Polaris app atempting to connect to Shopify with a Null value for the shop domain and everything breaks.

I've been able to prevent this by having the render function return Null on the first render but this feels kind of hacky

 render() {   

    if (typeof this.state.shop === 'undefined') {/* true on first render */}
      return null; { /* prevent rendering at this stage */ }

    return (
      <AppProvider
        shopOrigin={this.state.shop}
        debug={true}
      > 

Here is the async function that I'm using to get the domain from the server

 constructor() {
    super();

    this.state = {};
  }

  componentDidMount() {
    this.callApi()
      .then(res => this.setState(res))
      .catch(err => console.log(err));
  }

  callApi = async () => {
    const response = await fetch('/shopify/config');
    const body = await response.json();

    if (response.status !== 200) throw Error(body.message);

    return body;
  };

Solution

  • A simple way to do this is keeping an 'isLoading' attribute in your component's state, which is initially set to true.

    Once your component has finished fetching whatever data it needs to, call this.setState({isLoading: false}).

    Then your component's render method should look something like this:

    render() {
        const { isLoading } = this.state;
    
        if (isLoading) {
            return <span>loading...</span>
        }
    
        return <AppProvider {...props}/>
    }
    

    This way your app won't try to connect to the Shopify service until it is done fetching data.

    Of course you can make the return value of render whatever you like, maybe put a loading icon/animation in there, or just return an empty <div>.