davide-scalzo / react-native-mixpanel

A React Native wrapper for Mixpanel tracking
MIT License
455 stars 195 forks source link

Mixpanel instance was not initialized yet. Please run initialize() and wait for its promise to resolve before calling track(...) #150

Closed imdadahad closed 6 years ago

imdadahad commented 6 years ago

We have just recently started seeing this error.

Our initialisation of Mixpanel appears in our app initialisation, well before we call .track() anywhere.

index.js
class MainApp extends Component {
    constructor(props){
        super(props)
        Bugsnag.init()
                Mixpanel.sharedInstanceWithToken(mixpanelAPIToken)
    }

We are using "react-native-mixpanel": "1.0.2"

Any ideas?

EDIT: As a temporary solution, is there a way to check if the Mixpanel instance has been initialised. This way we can add a short-term fix where we would init if this is not the case before making any other calls.

marciok commented 6 years ago

I'm stuck in the same problem, I used await inside an async function, however got the same crash.

Tracker.start = async function(){
  if (__DEV__) {
    console.log('Start tracking in debug');

    return
  }

  await Mixpanel.sharedInstanceWithToken(MY_TOKEN);
}
Ingibjorg commented 6 years ago

You need to make sure that the Mixpanel instance has been initialized before calling other methods. The method sharedInstanceWithToken returns a promise.

It's nicely explained over here: https://github.com/davodesign84/react-native-mixpanel/pull/123

To account for this in my project, I created a class to handle this:

class AnalyticsManager {
  constructor() {
    this.mixpanel = callback => Mixpanel.sharedInstanceWithToken(Config.MIXPANEL_API_KEY)
      .then(() => callback())
      .catch(error => console.log('Failed to initialize Mixpanel: ', error));
  }

  track = async (event) => {
    this.mixpanel(() => Mixpanel.track(event));
  };
}

export default new AnalyticsManager();

Usage:

import AnalyticsManager from './AnalyticsManager';

AnalyticsManager.track('someEvent');
DataGreed commented 5 years ago

@Ingibjorg thanks!

Just in case someone needs the support for all methods, I've set up this class based on this comment (not an expert in JS, so not sure if there was a simpler way to do this):

import Mixpanel from 'react-native-mixpanel';

class AnalyticsManager {
    constructor() {
        this.mixpanel = callback => Mixpanel.sharedInstanceWithToken(YOUR_TOKEN)
            .then(() => callback())
            .catch(error => console.log('Failed to initialize Mixpanel: ', error));
    }

    /*
    Send and event name with no properties
        event: string
     */
    track = async (event) => {
        this.mixpanel(() => Mixpanel.track(event));
    };

    /*
    Track event with properties
        event: string
        properties: Object
     */
    trackWithProperties = async (event, properties) => {
        this.mixpanel(() => Mixpanel.trackWithProperties(event, properties));
    };

    /*
    Get the last distinct id set with identify or, if identify hasn't been
    called, the default mixpanel id for this device, e.g getDistinctId(function(id){})
        id: Function
     */
    getDistinctId = async (callback) => {
        this.mixpanel(() => Mixpanel.getDistinctId(callback));
    };

    //TODO: add docs (no docs in davodesign84/react-native-mixpanel)
    getSuperProperty = async (propertyName, callback) => {
        this.mixpanel(() => Mixpanel.getSuperProperty(propertyName, callback));
    };

    //TODO: add docs (no docs in davodesign84/react-native-mixpanel)
    flush = async () => {
        this.mixpanel(() => Mixpanel.flush());
    };

    //TODO: add docs (no docs in davodesign84/react-native-mixpanel)
    disableIpAddressGeolocalization = async () => {
        this.mixpanel(() => Mixpanel.disableIpAddressGeolocalization());
    };

    /*
    Create Alias from unique id, i.e. create a new mixpanel profile:
    to call when a user signs up, with a unique id that is not used
    by another mixpanel profile as param
        alias: string
     */
    createAlias = async (alias) => {
        this.mixpanel(() => Mixpanel.createAlias(alias));
    };

    /*
    Identify, i.e. associate to an existing mixpanel profile:
    to call when a user logs in and is already registered in Mixpanel
    with this unique id
     */
    identify = async (userId) => {
        this.mixpanel(() => Mixpanel.identify(userId));
    };

    /*
    Sets the start time for an action, for example uploading an image:
        timeEvent("Image Upload");
    to be followed by a tracking event to define the end time:
        track("Image Upload");

        event: string
     */
    timeEvent = async (event) => {
        this.mixpanel(() => Mixpanel.timeEvent(event));
    };

    /*
    Register super properties
        properties: Object
     */
    registerSuperProperties = async (properties) => {
        this.mixpanel(() => Mixpanel.registerSuperProperties(properties));
    };

    /*
    Register super properties Once
     */
    registerSuperPropertiesOnce = async (properties) => {
        this.mixpanel(() => Mixpanel.registerSuperPropertiesOnce(properties));
    };

    /*
    Android-only
    tell Mixpanel which user record in People Analytics should receive
    the messages when they are sent from the Mixpanel app,
    make sure you call this right after you call `identify`
     */
    initPushHandling = async (token) => {
        this.mixpanel(() => Mixpanel.initPushHandling(token));
    };

    /*
    Set People properties (warning: if no mixpanel profile has been assigned
    to the current user when this method is called, it will automatically
    create a new mixpanel profile and the user will no longer be anonymous in Mixpanel)
     */
    set = async (properties) => {
        this.mixpanel(() => Mixpanel.set(properties));
    };

    /*
    Set People Properties Once (warning: if no mixpanel profile has been assigned
    to the current user when this method is called, it will automatically
    create a new mixpanel profile and the user will no longer be anonymous in Mixpanel)
     */
    setOnce = async (properties) => {
        this.mixpanel(() => Mixpanel.setOnce(properties));
    };

    //TODO: add docs (no docs in davodesign84/react-native-mixpanel)
    removePushDeviceToken = async (deviceToken) => {
        this.mixpanel(() => Mixpanel.removePushDeviceToken(deviceToken));
    };

    //TODO: add docs (no docs in davodesign84/react-native-mixpanel)
    removeAllPushDeviceTokens = async () => {
        this.mixpanel(() => Mixpanel.removeAllPushDeviceTokens());
    };

    /*
    Track Revenue
        charge: Number
     */
    trackCharge = async (charge) => {
        this.mixpanel(() => Mixpanel.trackCharge(charge));
    };

    /*
    Track Revenue with properties
        charge: Number
        properties: Object
     */
    trackChargeWithProperties = async (charge, properties) => {
        this.mixpanel(() => Mixpanel.trackChargeWithProperties(charge, properties));
    };

    /*
    increment property
        propertyName: string
        by: Number
     */
    increment = async (propertyName, by) => {
        this.mixpanel(() => Mixpanel.increment(propertyName, by));
    };

    //TODO: add docs (no docs in davodesign84/react-native-mixpanel)
    union = async (name, properties) => {
        this.mixpanel(() => Mixpanel.union(name, properties));
    };

    /*
    iOS-only
    Add device token for push notifications
        token: string
     */
    addPushDeviceToken = async (token) => {
        this.mixpanel(() => Mixpanel.addPushDeviceToken(token));
    };

}

export default new AnalyticsManager();
siquick commented 4 years ago

@Ingibjorg thanks!

Just in case someone needs the support for all methods, I've set up this class based on this comment (not an expert in JS, so not sure if there was a simpler way to do this):

import Mixpanel from 'react-native-mixpanel';

class AnalyticsManager {
    constructor() {
        this.mixpanel = callback => Mixpanel.sharedInstanceWithToken(YOUR_TOKEN)
            .then(() => callback())
            .catch(error => console.log('Failed to initialize Mixpanel: ', error));
    }

    /*
    Send and event name with no properties
        event: string
     */
    track = async (event) => {
        this.mixpanel(() => Mixpanel.track(event));
    };

    /*
    Track event with properties
        event: string
        properties: Object
     */
    trackWithProperties = async (event, properties) => {
        this.mixpanel(() => Mixpanel.trackWithProperties(event, properties));
    };

    /*
    Get the last distinct id set with identify or, if identify hasn't been
    called, the default mixpanel id for this device, e.g getDistinctId(function(id){})
        id: Function
     */
    getDistinctId = async (callback) => {
        this.mixpanel(() => Mixpanel.getDistinctId(callback));
    };

    //TODO: add docs (no docs in davodesign84/react-native-mixpanel)
    getSuperProperty = async (propertyName, callback) => {
        this.mixpanel(() => Mixpanel.getSuperProperty(propertyName, callback));
    };

    //TODO: add docs (no docs in davodesign84/react-native-mixpanel)
    flush = async () => {
        this.mixpanel(() => Mixpanel.flush());
    };

    //TODO: add docs (no docs in davodesign84/react-native-mixpanel)
    disableIpAddressGeolocalization = async () => {
        this.mixpanel(() => Mixpanel.disableIpAddressGeolocalization());
    };

    /*
    Create Alias from unique id, i.e. create a new mixpanel profile:
    to call when a user signs up, with a unique id that is not used
    by another mixpanel profile as param
        alias: string
     */
    createAlias = async (alias) => {
        this.mixpanel(() => Mixpanel.createAlias(alias));
    };

    /*
    Identify, i.e. associate to an existing mixpanel profile:
    to call when a user logs in and is already registered in Mixpanel
    with this unique id
     */
    identify = async (userId) => {
        this.mixpanel(() => Mixpanel.identify(userId));
    };

    /*
    Sets the start time for an action, for example uploading an image:
        timeEvent("Image Upload");
    to be followed by a tracking event to define the end time:
        track("Image Upload");

        event: string
     */
    timeEvent = async (event) => {
        this.mixpanel(() => Mixpanel.timeEvent(event));
    };

    /*
    Register super properties
        properties: Object
     */
    registerSuperProperties = async (properties) => {
        this.mixpanel(() => Mixpanel.registerSuperProperties(properties));
    };

    /*
    Register super properties Once
     */
    registerSuperPropertiesOnce = async (properties) => {
        this.mixpanel(() => Mixpanel.registerSuperPropertiesOnce(properties));
    };

    /*
    Android-only
    tell Mixpanel which user record in People Analytics should receive
    the messages when they are sent from the Mixpanel app,
    make sure you call this right after you call `identify`
     */
    initPushHandling = async (token) => {
        this.mixpanel(() => Mixpanel.initPushHandling(token));
    };

    /*
    Set People properties (warning: if no mixpanel profile has been assigned
    to the current user when this method is called, it will automatically
    create a new mixpanel profile and the user will no longer be anonymous in Mixpanel)
     */
    set = async (properties) => {
        this.mixpanel(() => Mixpanel.set(properties));
    };

    /*
    Set People Properties Once (warning: if no mixpanel profile has been assigned
    to the current user when this method is called, it will automatically
    create a new mixpanel profile and the user will no longer be anonymous in Mixpanel)
     */
    setOnce = async (properties) => {
        this.mixpanel(() => Mixpanel.setOnce(properties));
    };

    //TODO: add docs (no docs in davodesign84/react-native-mixpanel)
    removePushDeviceToken = async (deviceToken) => {
        this.mixpanel(() => Mixpanel.removePushDeviceToken(deviceToken));
    };

    //TODO: add docs (no docs in davodesign84/react-native-mixpanel)
    removeAllPushDeviceTokens = async () => {
        this.mixpanel(() => Mixpanel.removeAllPushDeviceTokens());
    };

    /*
    Track Revenue
        charge: Number
     */
    trackCharge = async (charge) => {
        this.mixpanel(() => Mixpanel.trackCharge(charge));
    };

    /*
    Track Revenue with properties
        charge: Number
        properties: Object
     */
    trackChargeWithProperties = async (charge, properties) => {
        this.mixpanel(() => Mixpanel.trackChargeWithProperties(charge, properties));
    };

    /*
    increment property
        propertyName: string
        by: Number
     */
    increment = async (propertyName, by) => {
        this.mixpanel(() => Mixpanel.increment(propertyName, by));
    };

    //TODO: add docs (no docs in davodesign84/react-native-mixpanel)
    union = async (name, properties) => {
        this.mixpanel(() => Mixpanel.union(name, properties));
    };

    /*
    iOS-only
    Add device token for push notifications
        token: string
     */
    addPushDeviceToken = async (token) => {
        this.mixpanel(() => Mixpanel.addPushDeviceToken(token));
    };

}

export default new AnalyticsManager();

This should be the default implementation IMO - great work @DataGreed

raphaelrk commented 4 years ago

Thanks @DataGreed! Also added this to the class:

  reset = async () => {
    this.mixpanel(() => Mixpanel.reset());
  };