Adding Google Analytics to React - reactjs

I am trying to add Google Analytics to a React Web Application.
I know how to do it in HTML/CSS/JS sites and I have integrated it in an AngularJS app too. But, I'm not quite sure how to go about it when it comes to react.
With HTML/CSS/JS, I had just added it to every single page.
What I had done with AngularJS was adding GTM and GA script to index.html and added UA-labels to the HTML divs (and buttons) to get clicks.
How can I do that with React?
Please help!

Update: Feb 2019
As I saw that this question is being searched a lot, I decided to expand my explanation.
To add Google Analytics to React, I recommend using React-GA.
Add by running:
npm install react-ga --save
Initialization:
In a root component, initialize by running:
import ReactGA from 'react-ga';
ReactGA.initialize('Your Unique ID');
To report page view:
ReactGA.pageview(window.location.pathname + window.location.search);
To report custom event:
ReactGA.event({
category: 'User',
action: 'Sent message'
});
More instructions can be found in the github repo
The best practice for this IMO is using react-ga.
Have a look at the github rep

If you prefer not to use a package this is how it can work in a react application.
Add the "gtag" in index.html
<!-- index.html -->
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag("js", new Date());
gtag("config", "<GA-PROPERTYID>");
</script>
In the submit action of the login form, fire off the event
window.gtag("event", "login", {
event_category: "access",
event_label: "login"
});

Without using a package this is how I would do it:
In your index.js (in the render method):
{/* Global site tag (gtag.js) - Google Analytics */}
<script
async
src="https://www.googletagmanager.com/gtag/js?id=YOUR_TRACKING_ID"
/>
<script>{injectGA()}</script>
And outside the class:
const injectGA = () => {
if (typeof window == 'undefined') {
return;
}
window.dataLayer = window.dataLayer || [];
function gtag() {
window.dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'YOUR_TRACKING_ID');
};

One other great library that you can check is redux-beacon.
It gets integrated very easily with react/redux application and has a great documentation for it. ReactGA is good too but with redux-beacon, you won't clutter your app code with google analytics code as it works via its own middleware.

Escape the analytics code with dangerouslySetInnerHTML
First you have of course to share the header code to all pages, e.g. as asked at: React js do common header
Then, this Next.js answer https://stackoverflow.com/a/24588369/895245 gives a good working code that should also work outside of Next.js. It escapes the analytics code with dangerouslySetInnerHTML:
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-47867706-3"></script>
<script
dangerouslySetInnerHTML={{
__html: `window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-47867706-3', { page_path: window.location.pathname });
`,
}}
/>
where you should replace UA-47867706-3 with your own code.
This code is exactly the code that Google gives, but with the following modification: we added the:
{ page_path: window.location.pathname }
to gtag('config' for it to be able to get the visited path, since this is a JavaScript SPA.
This generates the desired output on the browser:
<script async="" src="https://www.googletagmanager.com/gtag/js?id=UA-47867706-3"></script><script>window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-47867706-3', { page_path: window.location.pathname });
</script>
The only other divergence from the exact code given by Google is the async="" vs async, but both of those are equivalent in HTML since it is a boolean attribute, see also: What's the proper value for a checked attribute of an HTML checkbox?
Escaping with dangerouslySetInnerHTML is necessary because otherwise React interprets the code inside script a JSX and that fails with:
Syntax error: Unexpected token, expected "}"
21 | <script>
22 | window.dataLayer = window.dataLayer || [];
> 23 | function gtag(){dataLayer.push(arguments);}
| ^
24 | gtag('js', new Date());
25 |
26 | gtag('config', 'UA-47867706-3');
I wish they would just automatically escape stuff inside script for us.
Finally to get page switches, you also have to track that with more code, see the Next.js answer mentioned above for an example.
Related: Adding script tag to React/JSX
Tested on react 17.0.2, next.js 10.2.2.

There are 2 types of Google Analytics properties: Universal Analytics (UA-xxxxxxxxx-x) which is deprecated with the end of life on 2023.07.01 and Google Analytics 4 property (G-xxxxxxxxxx) which is the replacement.
react-ga was popular for Universal Analytics but the maintainer doesn't plan to update it (related issues: 1, 2, 3) and it had maintenance issues (1). react-ga4 and ga-4-react popped up as replacements but since these are similar wrappers you're at the mercy of the maintainers to implement and support all functionality.
The simplest way to get started is to follow Google's guide: include gtag on the page and use it as window.gtag. This method works for both old and new tags and there's even TypeScript support via #types/gtag.js. The script can be loaded async as recommended.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<!-- ... -->
<script
async
src="https://www.googletagmanager.com/gtag/js?id=G-xxxxxxxxxx" >
</script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-xxxxxxxxxx')
</script>
<!-- ... -->
</head>
<body>
<!-- ... -->
</body>
</html>
Keep in mind that Google Analytics does automatic page tracking, but this will not work for every use case. For example, hash and search parameter changes are not tracked. This can lead to a lot of confusion. For example, when using HashRouter or anchor links the navigation will not be tracked. To have full control over page view tracking you can disable automatic tracking. See for a detailed explanation: The Ultimate Guide to Google Analytics (UA & GA4) on React (Or Anything Else
Manual page tracking: https://stackoverflow.com/a/63249329/2771889
You can see this working in cra-typescript-starter where I'm also setting the tag from an env var.

I suggest embedding the Segment script into your index.html, use the analytics library that is accessible on the window object, and add tracking calls onto React’s event handlers:
export default class SignupButton extends Component {
trackEvent() {
window.analytics.track('User Signup');
}
render() {
return (
<button onClick={this.trackEvent}>
Signup with Segment today!
</button>
);
}
}
I’m the maintainer of https://github.com/segmentio/analytics-react. I recommend checking it out if you want to solve this problem by using one singular API to manage your customer data, and be able to integrate into any other analytics tool (we support over 250+ destinations) without writing any additional code. 🙂

Looking at google's site https://developers.google.com/analytics/devguides/collection/analyticsjs,
you could also add Google Analytics using this function:
const enableGA = () => {
!function(A,n,g,u,l,a,r){A.GoogleAnalyticsObject=l,A[l]=A[l]||function(){
(A[l].q=A[l].q||[]).push(arguments)},A[l].l=+new Date,a=n.createElement(g),
r=n.getElementsByTagName(g)[0],a.src=u,r.parentNode.insertBefore(a,r)
}(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-XXXXXXXX-X');
ga('send', 'pageview');
}
This way you don't need an external library, and it's pretty quick to setup.

Related

React onClick not working in any of my browsers, but for colleagues it does

I've created an onClick handler in a very simple React function component:
export default function MyButton() {
return (
<button
onClick={() => {
console.log('test');
}}
>
Button
</button>
);
}
Now the weird part: no matter what browser I use, the event is not firing. I've created such a component hundreds of times and everything was good, until now.
For everyone else this code works, as it was intended.
I cannot share the whole project or an example repository. It's really nothing but a simple React app you see everywhere.
What could be the reason for why it's not working on my system?
EDIT:
The error was somehow within yarn. I called webpack-dev-server -d source-map --mode=development for development and I am using "webpack-dev-server": "^4.0.0-beta.0". I think the cache could've gotten corrupted somehow.
To fix it, I removed my output directory and started the script with npm instead of yarn. This way it worked, even when I use yarn again.
I really don't know why this happened. Would be happy to know why.
I also faced the same issue and the reason of the issue (in my case , probably yours ) is HtmlWebpackPlugin, HtmlWebpack Plugin is adding a addition script tag of bundle in head tag of index.html.
my html
<html>
<head>
<title>my-react-app</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
htmlwebpackplugin generated html
<html>
<head>
<title>my-react-app</title>
<script defer src="bundle.js"></script></head>
<body>
<div id="root"></div>
<script src="bundle.js"></script>
</body>
</html>
because of this additional script tag, there was a problem in react (i read a post on reddit regarding to this problem and he also have multiple script of same bundle and he was having the same problem), i solved it by deleting my script tag, but we can use copywebpack plugin to just copy html. Or other solution is to configure htmlwebpackplugin suck a way that it will not inject any addition tags
...
new HtmlWebpackPlugin({
name: "index.html",
inject: false,
template: path.resolve(__dirname, "public/index.html"),
}),
...
Use named function instead of anonymous function. Named functions are very useful for identifying what functions caused errors during development as well as when retrieving logs from your users.
import React from "react";
export default function MyButton() {
const handleChange = () => {
console.log("test");
};
return <button onClick={handleChange}>Button</button>;
}
It is a good practice to name-all-functions for a better developer debugging (and development) experience which anonymous function does not provide.
For more clarification between Named and Anonymous function Learn the benefits of Named vs Anonymous function here
Try typing your function as React.FC.
Create a typescript (tsx) file and use the upcoming code:
import React from "react";
export const MyButton: React.FC = () => {
return (
<button
onClick={() => {
console.log("test");
}}
>
Button
</button>
);
};
Note that using this code, you are typing the component making sure that your function is typed as React.FunctionComponent.
Did you import this in your file, if not then add this tine on top
import React from 'react';

how to include webpack in in-browser Babel?

I am newbie to React.js. For learning purpose just I created the login page which you can find here.
In my local project also, I used the CDN for babel and react, like below.
<script src="https://unpkg.com/react#latest/dist/react.js"></script>
<script src="https://unpkg.com/react-dom#latest/dist/react-dom.js"></script>
<script src="https://unpkg.com/babel-standalone#6.15.0/babel.min.js"></script>
I want to use one of the form validation plugin into this example. But when I tried to include this (as per the document)
import ValidateableForm from 'react-form-validate';
I am getting the following error.
Uncaught ReferenceError: require is not defined
I went through few posts and they said that I have to use webpack or Rollup or Browsify .I am not sure how to include this in to my current local project setup. Since I am not using npm (in learing I dont want to use npm)
I dont know how to include that plugin into my project
If it is already coming with external site , I cant able to figure
it out what is the issue.
Please help me to resolve the issue.
This is an old question, but at least currently, this is completely possible with conventional script tags. This article is very helpful for understanding development setup alternatives for React
In that article it details bringing in React, React-Dom, and Babel via script tags for development, like this (I'm linking to npm downloaded packages, but that isn't necessary):
<script src="/node_modules/react/umd/react.development.js"></script>
<script src="/node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="/node_modules/#babel/standalone/babel.min.js"></script>
In my case, I needed to bring in the react-notification-system plugin:
<script src="/node_modules/react-notification-system/dist/react-notification-system.min.js"></script>
(Note the use of the compiled 'dist' version)
Once that was included I was able use it like this:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.notificationSystem = new ReactNotificationSystem();
}
addNotification = event => {
event.preventDefault();
const notification = this.notificationSystem.current;
notification.addNotification({
message: 'Notification message',
level: 'success'
});
};
render() {
return (
<div>
<button onClick={this.addNotification}>Add notification</button>
<ReactNotificationSystem ref={this.notificationSystem} />
</div>
);
}
}
I had to look in the plugin's code to know that the name: ReactNotificationSystem would be available, much of the plugin documentation that you find is not written with this type of dev setup in mind, but it does work.

No Permission error using Firebase

I am trying to add Firebase for the first time to an ionic application. I have set up my project and referenced the javascript files however when I run the application I see the error 'Error: Permission Denied'
I have not yet set up authentication in my app but, if I am understanding things properly I shouldn't need to do this while I am just testing.
My index.html contains the following:
<!-- ionic/angularjs js -->
<script src="lib/ionic/js/ionic.bundle.js"></script>
<!-- Firebase -->
<script src="https://cdn.firebase.com/js/client/2.2.4/firebase.js"></script>
<!-- AngularFire -->
<script src="https://cdn.firebase.com/libs/angularfire/1.2.0/angularfire.min.js"></script>
While the app.js has the following lines:
angular.module('starter', ['ionic', 'firebase'])
.factory("Items", function($firebaseArray) {
var itemsRef = new Firebase("https://todo-83e58.firebaseio.com/items");
return $firebaseArray(itemsRef);
})
.controller('ListCtrl', function($scope, $ionicListDelegate, Items) {
$scope.items = Items;
$scope.addItem = function () {
var name = prompt("what do you need to buy?");
if (name) {
$scope.items.$add({"name": name});
}
};
I would be grateful for any pointers on how to get the basics right as I have tried following the information on the Firebase site and I seem to have it all set up correctly. Am I misunderstanding the authentication or shouldI be able to write to the database without setting this up first ?
It sounds that your Security & Rules isn't giving you access to items. Please make sure to spend some time reading the S&R documentation.
If you are only testing the application and don't want to care about it now just set it to full read and write.
On the new console go to Database > Rules.
{
"rules": {
".read": true,
".write": true
}
}

referencing an amd module(arcgis) in webpack app

I'm building a react app with webpack and i need to incorporate arcgis maps into a react component. I have know idea how to bring this into my project. I've tried creating an arcgis directory with an index.js of the built javascript and trying to reference that:
import {Map} from 'arcgis/index'
That doesn't work. I then just tried to include the css/js script tags directly into my index.html but when I try to require them, like in the example, webpack obviously can't find them. Is there some way to tell webpack to ignore require calls in my src file so it gets handled by the browser? I'm trying and failing at doing the following:
import React from 'react'
export default class EsriMap extends React.Component {
componentDidMount() {
const _this = this
require(["esri/map", "dojo/domReady!"], function(Map) {
var map = new Map(_this.refs.map, {
center: [-118, 34.5],
zoom: 8,
basemap: "topo"
});
});
}
render() {
return (
<div ref="map"></div>
)
}
}
You may want to try this https://github.com/tomwayson/esri-webpack-babel .
This method is nice because it doesn't bog down the build. You pull in the ESRI Api from the CDN, and tell webpack that it's an external.
//Add this...
externals: [
// Excludes any esri or dojo modules from the bundle.
// These are included in the ArcGIS API for JavaScript,
// and its Dojo loader will pull them from its own build output
function (context, request, callback) {
if (/^dojo/.test(request) ||
/^dojox/.test(request) ||
/^dijit/.test(request) ||
/^esri/.test(request)
) {
return callback(null, "amd " + request);
}
callback();
}
],
//And this to you output config
output: {
libraryTarget: "amd"
},
When your app loads you bootstrap you webpack modules using Dojo in a script tag.
<!-- 1. Configure and load ESRI libraries -->
<script>
window.dojoConfig = {
async: true
};
</script>
<script src="https://js.arcgis.com/4.1/"></script>
<!-- Load webpack bundles-->
<script>
require(["Angular/dist/polyfills.bundle.js", "Angular/dist/vendor.bundle.js", "Angular/dist/app.bundle.js"], function (polyfills, vendor, main) { });
</script>
I've got it working with an Angular 2 App I'm working on. The only downside is I haven't yet got the unit tests to run right using Karma. I've only been working on that a few hours now.. Hope to have a solution to the testing issue soon.
#getfuzzy's answer will work well as long as you don't need to lazy load the ArcGIS API (say for example only on a /map route).
For that you will want to take the approach I describe in this answer
This blog post explains why you need to use one of these two approaches and explains how they work as well as the pros/cons of each.
I think you can try using bower version of esrijsapi. Doc link

Insert Google Adsense code in Ionic breaks display

In this simple Codepen demo, I have a bar and a slider. Works fine, but if I add the Google Adsense code, then display is broken. How should I insert the Adsense code to display the Ad at the top of the web page.
What I mean by "display is broken": the bar overlays the start of the ion-content (hello1 hello2). I already put class="has-header" in the <ion-content>.
NB I use Ionic framework both for the mobile site, and (next step) for the hybrid app. I don't expect the ad to work in the native code. I just need to display the Adsense ad on the mobile web site.
Code of the ad is:
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<!-- hybride_320x50 -->
<ins class="adsbygoogle"
style="display:inline-block;width:320px;height:50px"
data-ad-client="ca-pub-4134311660880303"
data-ad-slot="1861097476"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
Skeleton of the ionic page:
header / angularjs
Google ad
bar
ion-content
content
slider
content
I would recommend you to use AdMob. I've written this native plugin which supports ionic: https://github.com/appfeel/admob-google-cordova/wiki/Angular.js,-Ionic-apps. Moreover it will also let you show interstitials.
ionic plugin add cordova-admob
<script src="lib/angular-admob/angular-admob.js"></script>
<script>
var app = angular.module('myApp', ['admobModule']);
app.config(['admobSvcProvider', function (admobSvcProvider) {
// Optionally you can configure the options here:
admobSvcProvider.setOptions({
publisherId: "ca-app-pub-XXXXXXXXXXXXXXXX/BBBBBBBBBB",
interstitialAdId: "ca-app-pub-XXXXXXXXXXXXXXXX/IIIIIIIIII",
autoShowBanner: true,
autoShowInterstitial: true
});
}]);
app.run(['admobSvc', function (admobSvc) {
admobSvc.createBannerView();
admob.requestInterstitialAd();
// Handle events:
$rootScope.$on(admobSvc.events.onAdOpened, function onAdOpened(evt, e) {
console.log('adOpened: type of ad:' + e.adType);
});
}]);
</script>
one fast solution is add the following class to your style
.adsbygoogle {
position: absolute;
z-index: 10;
}

Resources