title: Static functions called twice in React Native
body: <p>To improve the structure of my app I'm moving most In App Purchase functionality into a special class with static methods and variables.</p>
  
  <p>When I try to use this class <strong>the functions seem to get called twice each, even though in my code I only call them once</strong>. When I debugged it with remote debugging in Chrome and a breakpoint at (1) it did reach that breakpoint twice and both times when I looked at the stacktrace it was called from <code>componentDidMount()</code> in <code>App.js</code>.</p>
  
  <p>In <code>App.js</code> I have this:</p>
  
  <pre><code>import IAPManager from "./helpers/IAPManager";
  ...
  async componentDidMount() {
          await IAPManager.prepare(); // (1) debug breakpoint
  }
  
  async componentWillUnmount() {
      await IAPManager.endConnection();
  }
  </code></pre>
  
  <p>And I also call the methods of <code>IAPManager</code> in another component:</p>
  
  <pre><code>import IAPManager from "../../helpers/IAPManager";
  ...
  async componentDidMount() {
          const isConnected = await NetInfo.isConnected.fetch();
          if (!isConnected) {
              console.log("Not connected to the internet");
              Alert.alert(
                  "No connection",
                  "Uh oh, looks like you aren't connected to the internet, which you need to be to take advantage of Premium",
                  [{text: "OK"}]
              );
          } else {
              let hasPurchasedPremium;
              try {
                  hasPurchasedPremium = await IAPManager.isPremiumUnlocked();
              } catch (e) {
                  console.log("Failed to check if premium is unlocked", e);
                  hasPurchasedPremium = false;
              }
  
              this.setState({ hasPurchasedPremium: hasPurchasedPremium });
              if (hasPurchasedPremium) {
                  console.log("Has purchased premium");
              } else {
                  console.log("Hasn't purchased premium");
                  const premiumProduct = await IAPManager.getPremiumProductInfo();
                  this.setState({ premiumProduct: premiumProduct });
              }
          }
      }
  </code></pre>
  
  <p>The <code>IAPManager</code> class looks like this:</p>
  
  <pre><code>import * as InAppPurchase from 'react-native-iap';
  import { AsyncStorage, Platform } from "react-native";
  
  const itemSKUs = Platform.select({
      android: [
          "unlock_premium"
      ]
  });
  
  export default class IAPManager {
      static hasUnlockedPremium = undefined;
      static productInfo = undefined;
  
      static async prepare() {
          await InAppPurchase.prepare();
          console.log("done preparing");
      }
  
      static async endConnection() {
          await InAppPurchase.endConnection();
      }
  
      static async getProductInfo() {
          console.log("getProductInfo");
          try {
              const products = await InAppPurchase.getProducts(itemSKUs);
              IAPManager.productInfo = products;
              console.log("finished call to get products", IAPManager.productInfo);
          } catch (e) {
              console.log(e);
          }
      }
  
      static async getPremiumProductInfo() {
          console.log("getPremiumProductInfo");
          if (IAPManager.productInfo === undefined) {
              await this.getProductInfo();
          }
          const premiumProduct = IAPManager.productInfo.filter(product =&gt; product.productId === "unlock_premium");
          return premiumProduct[0];
      }
  
      static async isPremiumUnlocked() {
          console.log("isPremiumUnlocked");
          if (IAPManager.hasUnlockedPremium !== undefined) {
              return IAPManager.hasUnlockedPremium;
          }
          IAPManager.hasUnlockedPremium = true; // TODO: remove this when done testing
          return IAPManager.hasUnlockedPremium;
      }
  }
  </code></pre>
  
  <p>And finally, the logs I'm getting look like this:</p>
  
  <pre><code>05-04 23:58:34.354 15867 20588 I ReactNativeJS: isPremiumUnlocked
  05-04 23:58:34.370 15867 20588 I ReactNativeJS: Hasn't purchased premium
  05-04 23:58:34.370 15867 20588 I ReactNativeJS: getPremiumProductInfo
  05-04 23:58:34.384 15867 20588 I ReactNativeJS: isPremiumUnlocked
  05-04 23:58:34.385 15867 20588 I ReactNativeJS: getProductInfo
  05-04 23:58:34.388 15867 20588 I ReactNativeJS: isPremiumUnlocked
  05-04 23:58:34.403 15867 20588 I ReactNativeJS: Hasn't purchased premium
  05-04 23:58:34.403 15867 20588 I ReactNativeJS: getPremiumProductInfo
  05-04 23:58:34.419 15867 20588 I ReactNativeJS: isPremiumUnlocked
  05-04 23:58:34.422 15867 20588 I ReactNativeJS: getProductInfo
  05-04 23:58:34.424 15867 20588 I ReactNativeJS: 'finished call to get products', [ { description: 'With premium you can set more than two timers at the same time.',
  05-04 23:58:34.424 15867 20588 I ReactNativeJS:     title: 'Unlock Premium',
  05-04 23:58:34.424 15867 20588 I ReactNativeJS:     localizedPrice: '19,00 kr',
  05-04 23:58:34.424 15867 20588 I ReactNativeJS:     type: 'inapp',
  05-04 23:58:34.424 15867 20588 I ReactNativeJS:     currency: 'SEK',
  05-04 23:58:34.424 15867 20588 I ReactNativeJS:     price: '19.00',
  05-04 23:58:34.424 15867 20588 I ReactNativeJS:     productId: 'unlock_premium' } ]
  05-04 23:58:34.426 15867 20588 I ReactNativeJS: done preparing
  05-04 23:58:34.666 15867 20588 I ReactNativeJS: 'finished call to get products', [ { description: 'With premium you can set more than two timers at the same time.',
  05-04 23:58:34.666 15867 20588 I ReactNativeJS:     title: 'Unlock Premium',
  05-04 23:58:34.666 15867 20588 I ReactNativeJS:     localizedPrice: '19,00 kr',
  05-04 23:58:34.666 15867 20588 I ReactNativeJS:     type: 'inapp',
  05-04 23:58:34.666 15867 20588 I ReactNativeJS:     currency: 'SEK',
  05-04 23:58:34.666 15867 20588 I ReactNativeJS:     price: '19.00',
  05-04 23:58:34.666 15867 20588 I ReactNativeJS:     productId: 'unlock_premium' } ]
  05-04 23:58:34.668 15867 20588 I ReactNativeJS: done preparing
  </code></pre>
  
  <p>Thanks for reading this far, and I appreciate all help :)!</p>