How to use Google Analytics with React?
Asked Answered
M

7

9

In my react app I have some pages:

  1. Main
  2. Service
  3. Contact
  4. Profile (private)
  5. etc..

I need to track users activity with Google Analytics. I googled react-ga and it's just fine. But with this library I have to initialize my GA on every route I use. For example:

Route "/" - main page:

class Main extends Component {

    componentDidMount() {
        initGA();
    }

    render() {
        return (
            <div>
                <Component1 />
                <Component2 />
            </div>
        )
    }
}

My initGA() looks like:

import ReactGA from 'react-ga';

export const initGA = () => {
    ReactGA.initialize('UA-00000000-1');
    ReactGA.pageview(window.location.pathname + window.location.search);
    console.log(window.location.pathname + window.location.search);
}

My App class looks like:

class App extends Component {
    render() {
        return (
            <BrowserRouter>
                <div className="App">

                    <Switch>
                        <Route exact path="/" component={Main} />
                        <Route exact path="/signup" component={SignupLayout} />
                        <Route component={PublicLayout} />
                    </Switch>

                </div>
            </BrowserRouter>
        );
    }
}

In my way of using react-ga I'm adding initGA() function on every component which renders on route response. I think it is not right to duplicate initGA() in every component. Please, guys, how do you use react-ga? What is right way to use react-ga?

Mccloskey answered 9/10, 2018 at 15:22 Comment(0)
M
11

To make it work need to use Router functionality. So in App component import { Router } from 'react-router-dom'. It has to be Router not BrowserRouter.

Then import createHistory from 'history/createBrowserHistory'

const history = createHistory()
ReactGA.initialize('UA-000000-1');
history.listen((location, action) => {
    ReactGA.pageview(location.pathname + location.search);
    console.log(location.pathname)
});

This code will fire on every route change!

Than give a history attribute to your Router component.

Complete code:

import React, { Component } from 'react';
import { Router, Route, Switch } from 'react-router-dom';
import createHistory from 'history/createBrowserHistory'
import Main from './components/pages/Main';
import ReactGA from 'react-ga';


const history = createHistory()
ReactGA.initialize('UA-00000000-1');
history.listen((location, action) => {
    ReactGA.pageview(location.pathname + location.search);
    console.log(location.pathname)
});

class App extends Component {

    render() {
        return (

            <Router history={history}>
                <div className="App">

                    <Switch>
                        <Route exact path="/" component={Main} />
                        <Route exact path="/signup" component={SignupLayout} />
                        <Route component={PublicLayout} />
                    </Switch>

                </div>
            </Router>
        );
    }
}

export default App;
Mccloskey answered 9/10, 2018 at 16:38 Comment(4)
So using this, we don't need call initGA() on every component in componentDidMount() / componentWillMount() ?Yevetteyew
This answer is ok, but you won't save the first page load in the analytics. The history.listen will fire only when the route changes. This doesn't happen when you first load the page.Attractant
(not related to the question) but please don't create classes if you don't need them. Use a functional component instead. I personally love using class, but only when I need it.Malvaceous
@Attractant old comment, but maybe could help others: to register also the first page load just fire up the ReactGA.pageview function right after ReactGA.initialize, thats allDeragon
A
13

This is the way to do it with hooks.

App.js

import React, { useEffect } from 'react'
import { Router, Route } from 'react-router-dom'
import { createBrowserHistory } from 'history'
import ReactGA from 'react-ga'

ReactGA.initialize(process.env.REACT_APP_GA_TRACKING_NO)
const browserHistory = createBrowserHistory()
browserHistory.listen((location, action) => {
  ReactGA.pageview(location.pathname + location.search)
})

const App = () => {
  useEffect(() => {
    ReactGA.pageview(window.location.pathname + window.location.search)
  }, [])

  return <div>Test</div>
}

The answer above with the class components is almost fine, but you won't get to see the first page load in the analytics. The history.listen will fire only when the route changes. This doesn't happen when you first load the page. To fix this, I added a useEffect a hook in the App component. If the user reloads the site, the App will be always rerendered. ReactGA.pageview(window.location.pathname + window.location.search) makes sure that we attribute the reload to the correct route if the route is something else than '/'.

Attractant answered 13/9, 2019 at 12:19 Comment(3)
are you sure this works? Looks like useEffect will only be called once!Malvaceous
It has to called only once. The hook records only the FIRST page load. The rest of the page loads will be recorded in the browserHistory.listen even listener.Attractant
I used your solution and it works great for me. Thank you. +1Malvaceous
M
11

To make it work need to use Router functionality. So in App component import { Router } from 'react-router-dom'. It has to be Router not BrowserRouter.

Then import createHistory from 'history/createBrowserHistory'

const history = createHistory()
ReactGA.initialize('UA-000000-1');
history.listen((location, action) => {
    ReactGA.pageview(location.pathname + location.search);
    console.log(location.pathname)
});

This code will fire on every route change!

Than give a history attribute to your Router component.

Complete code:

import React, { Component } from 'react';
import { Router, Route, Switch } from 'react-router-dom';
import createHistory from 'history/createBrowserHistory'
import Main from './components/pages/Main';
import ReactGA from 'react-ga';


const history = createHistory()
ReactGA.initialize('UA-00000000-1');
history.listen((location, action) => {
    ReactGA.pageview(location.pathname + location.search);
    console.log(location.pathname)
});

class App extends Component {

    render() {
        return (

            <Router history={history}>
                <div className="App">

                    <Switch>
                        <Route exact path="/" component={Main} />
                        <Route exact path="/signup" component={SignupLayout} />
                        <Route component={PublicLayout} />
                    </Switch>

                </div>
            </Router>
        );
    }
}

export default App;
Mccloskey answered 9/10, 2018 at 16:38 Comment(4)
So using this, we don't need call initGA() on every component in componentDidMount() / componentWillMount() ?Yevetteyew
This answer is ok, but you won't save the first page load in the analytics. The history.listen will fire only when the route changes. This doesn't happen when you first load the page.Attractant
(not related to the question) but please don't create classes if you don't need them. Use a functional component instead. I personally love using class, but only when I need it.Malvaceous
@Attractant old comment, but maybe could help others: to register also the first page load just fire up the ReactGA.pageview function right after ReactGA.initialize, thats allDeragon
M
5

Here is a solution utilising useLocation() released in React Router V5.1.

The benefit of this approach is that it is compatible with BrowserRouter if you wish to continue using that.

import React, { useEffect } from 'react';
import { useLocation,} from 'react-router-dom';
import ReactGA from 'react-ga';

// Init Google Analytics
ReactGA.initialize('UA-00000000-1');

const App = () => {
  const location = useLocation();

  // Fired on every route change
  useEffect(() => {
    ReactGA.pageview(location.pathname + location.search);
  }, [location]);

  return (
    <div className="App"></div>
  )
}
Marks answered 28/6, 2020 at 10:49 Comment(1)
This is the easiest implementation that works comprehensively at this point, thanks!Antefix
G
1

You should only init once, and then after that use

ReactGA.pageview(pagepath);

in your routing.

Demo: https://github.com/react-ga/react-ga/tree/master/demo

Gershwin answered 9/10, 2018 at 15:26 Comment(3)
What do you mean "in your routing"? Can you give more detailed example with code?Mccloskey
In this way tracking doesn't work. Gives a warning: react-ga.js:88 [react-ga] ReactGA.initialize must be called first or GoogleAnalytics should be loaded manuallyMccloskey
i added ReactGA.initialize on index.js and on every page ReactGA.pageview('...') but there is always an alert on console ReactGA.initialize must be called first or GoogleAnalytics should be loaded manually. So i decided to add on every page ReactGA.initializeTelic
A
1

you can solve this problem using service.js file where you have to made multiple expotrs

import ReactGA from 'react-ga';

export const GAInit = (TrackingID) => {
   ReactGA.initialize(TrackingID)
}

export const GAEvent = (category, action) => {
ReactGA.initialize('TrackingIDHere')   
ReactGA.event({
   category,
   action
})
}

// export pageview and more...

import from App.js and initialize and use services you wouldn't need to initialize everytime.

or you can use script instead of react-ga to solve this problem.

Affirmative answered 25/3, 2020 at 17:45 Comment(0)
A
0

I was not getting the paths (always '/') until I realised I was using HashRouter. If this is the case, then you can apply the following:

import React, { useState, useEffect } from 'react';
import { Route, Switch, NavLink, HashRouter as Router } from 'react-router-dom';
import ReactGA from 'react-ga';
import { createHashHistory } from 'history';

const App = () => {
   ReactGA.initialize(trackingId);
   const browserHistory = createHashHistory();
   browserHistory.listen((location, action) => {
      ReactGA.pageview(location.pathname + location.search);
})

// Activate Google Analytics
useEffect(() => {
    ReactGA.pageview(window.location.pathname + window.location.search);
}, []);
}

By the way, it is not recommended to put your Google Analytics tracking_id on the client-side. Below is the updated code in a more secure way, using axios to retrieve the tracking_id from the server:

    useEffect(() => {
        const getTrackingID = async () => {
            await axios.get(`${url}/ga/tracking_id`, {
               headers: { 'Content-Type': 'application/json' }
            })
            .then(res => {
                if (res.status === 204) {
                    console.log('No GA tracking_id found');
                } else if (res.status === 200) {
                    const trackingId = res.data;
                    ReactGA.initialize(trackingId);
                    // Capture 1st load in home page
                    ReactGA.pageview(window.location.pathname + window.location.search);
                    // Capture any subsequent page change
                    browserHistory.listen((location) => {
                        ReactGA.pageview(location.pathname + location.search);
                    });
                }
            })
            .catch(err => {
                console.log('Error while retrieving GA tracking_id', err);
            });
       };
       getTrackingID();
   }, []);
Auricula answered 18/9, 2020 at 18:55 Comment(0)
M
0

Here is how you solve avoiding to "initialize" GA on every component.

Basically, make sure that the first ReactGA script that runs is ReactGA.initialize('UA-00000000-1');

To ensure that you should place your initGA(); outside of componentDidMount(), and keep the rest of your ReactGA scripts inside of componentDidMount().

I know it is an old post but all of the answers addressed route tracking but not this actual issue.

Machinegun answered 11/11, 2021 at 13:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.