DavidWells / analytics

Lightweight analytics abstraction layer for tracking page views, custom events, & identifying visitors
https://getanalytics.io
MIT License
2.42k stars 244 forks source link

react native support #43

Open chrisdrackett opened 4 years ago

chrisdrackett commented 4 years ago

Currently if you run this in a react native project you get the following error:

TypeError: undefined is not an object (evaluating 'os.indexOf')

I'm guessing that the check to see if this is running in a browser is passing even though react native should probably be treated like a "server" like environment.

DavidWells commented 4 years ago

Oh interesting! I didn't know window acted like this in React Native.

Does https://github.com/DavidWells/analytics/pull/44 solve this issue for you when testing locally?

Looks like the code trips up here with the different navigator in RN https://github.com/DavidWells/analytics/blob/cca3bafda3f902dc6da484ec3d6245ccb6d12fe0/packages/analytics-core/src/utils/getOSName/browser.js#L5

chrisdrackett commented 4 years ago

I just did another pass in #44. I haven't tested it all out yet, but for the most part react native should act like the server implementation. For now I'm just getting my feet wet understanding the code. #44 is probably more heavy handed than it could be (there are things that can be done in React Native to get a lot of the stuff that isn't available in node.js) but for now I'd like to get things working first and foremost. I'll be spending more time on this tomorrow!

DavidWells commented 4 years ago

I'm wondering if we can do something like https://github.com/react-native-community/react-native-device-info/blob/master/src/web/index.js#L87-L106 to populate the correct info on react native. I haven't used react native in a while and I'm not sure whats possible.

I found this to check if we are running in RN context

if (typeof document != 'undefined') {
  // I'm on the web!
}
else if (typeof navigator != 'undefined' && navigator.product == 'ReactNative') {
  // I'm in react-native
}
else {
  // I'm in node js
}

I'm not 100% on if it works or not 😅

chrisdrackett commented 4 years ago

I'm pretty sure this works and is where I started, but I didn't realize how much document was used in others parts of the lib! This is why I went down my current route, but I'm not sure if that is the best way forward either :D

chrisdrackett commented 4 years ago

I think there are probably two levels of RN support:

  1. just get React Native working (probably making it look like a server)
  2. actually add rich react native support (fill out things like platform, width, height, etc.)

2 would be super cool, but would require using react native code like Platform and I'm not sure yet the best way to go about this.

chrisdrackett commented 4 years ago

I wonder if the best way to handle 2 is to have a way to plop things like platform, viewport size, etc. into the config and then just have react native users (via a package or by hand) throw those values in when initializing analytics

DavidWells commented 4 years ago
  1. just get React Native working (probably making it look like a server)
  2. actually add rich react-native support (fill out things like platform, width, height, etc.)

This sounds good to me 😃. I love the idea of RN support!

For # 2, We might need to set up another build to target react native... All in good time 😅

Does React native use any DOM APIs? If not, we might be able to simply add the react-native check to the isBrowser util then we wouldn't need to check both everywhere

chrisdrackett commented 4 years ago

yep! this is exactly what I did :)

as far as I can tell the issue now is that React Native is trying to use the browser version of plugins, which won't work as RN has no document or normal Dom for that matter: https://github.com/DavidWells/analytics/blob/master/packages/analytics-plugin-segment/src/browser.js#L118

DavidWells commented 4 years ago

I managed to spin up https://github.com/expo/create-react-native-app & test it out.

I see what you are saying, it appears react native is loading the browser version not the serverside implementation.

image

Everything work just work â„¢, if the analytics.es.js (serverside implementation) would load.

image

... This is perplexing.

I believe it has something to do with this mapping in package.json https://github.com/DavidWells/analytics/blob/master/packages/analytics-core/package.json#L28-L31

As you mentioned, this will affect all the plugins as well. We need to figure out a way to solve this so the serverside implementations are used when folks are running in React Native.

1 possible solution is publishing different modules for client + server implementations but I'd like to avoid this because of the additional maintenance burden.

DavidWells commented 4 years ago

In the meantime, as this gets resolved for the serverside plugins, you can inline your plugins & make API calls to remote analytics tools like this:

import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Analytics from 'analytics'

const analytics = Analytics({
  app: 'coolbeans',
  plugins: [{
    name: 'inlined-thing',
    page: ({ payload }) => {
      // make your remote call 
      console.log('page payload', payload)
    },
    track: ({ payload }) => {
       // make your remote call 
      console.log('track payload', payload)
    }
  }]
})

export default function App() {
  console.log(analytics.getState())
  setTimeout(() => {
    analytics.page()
    analytics.track('thing', {
      color: 'blue'
    })
  }, 300)
  return (
    <View style={styles.container}>
      <Text>Hi cool stuff</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});
gafner commented 4 years ago

Are you guys working on this or one should just work as suggested above with a serverside plugin?

DavidWells commented 4 years ago

Hey @gafner, I'm still trying to figure out how React-native resolves it's modules.

By default, it's loading the browser version of the analytics library and the browser version of plugins.

Do you know if there is a specific package.json field for the react-native bundler to use?


Are you guys working on this or one should just work as suggested above with a serverside plugin?

The above code should work for you in React-native. However, If you include @analytics/google-analytics for example, it will attempt to load the browser version instead of the node serverside version of the package

jgcmarins commented 4 years ago

How are you guys planning to support multiple storage ways? Like cookies, AsyncStorage or any other native storage? Is it a problem for the plugin land? Or is this something that analytics will handle?

DavidWells commented 4 years ago

@jgcmarins Do cookies work in React Native? If yes, then they are already in use there 😃

Under the hood, analytics tries to use these 3 mechanisms in this order:

  1. localStorage
  2. cookies
  3. global window object

https://github.com/DavidWells/analytics/blob/master/packages/analytics-util-storage/src/index.js

It's possible that we could expose this as an option where any method could be used like AsyncStorage.

I'm looking for React Native experts to help out here as I have little experience working with it myself. 😃

fernandopascoalbr commented 4 years ago

@DavidWells This lib is a good one and would solve many react-native problems. We currently use GA for firebase and there are many differences mainly in the reports.

fernandopascoalbr commented 4 years ago

@chrisdrackett i forked the react-native-google-analytics and adjust to send metrics. The firebase report is very different and you can see web report with this lib. it worked well for me. You can see here and install like this:

"react-native-google-analytics": "https://github.com/fernando-pascoal/react-native-google-analytics.git"
cheynewallace commented 2 years ago

Is this still actively being looked at? I just hit this problem with our React native app, thinking I could use this library but it seems to be looking for a window.location object, which of course doens't exist so the app crashes. Could we not just have a config property to enforce server side tracking rather than relying on detection that seems to not work?
ie


const analytics = analyics({
    app: "some-app",
        client: "server",
    plugins: [
        googleAnalytics({
            trackingId: "xxxx",
        }),
    ],
});```
johnsoncwb commented 2 years ago

Hello folks, i'm trying to add a GA4 plugin for react-native but i receiving this error:

Screen Shot 2022-05-04 at 21 48 46

do you guys have plans to add extra verifications to work in mobile apps?

oddnavy commented 1 year ago

@johnsoncwb did you manage to work around this error and get it running?

DavidWells commented 1 year ago

Possibly fixed via https://github.com/DavidWells/analytics/pull/367 released with analytics@0.8.7