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?

0

7 Answers 7

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 '/'.

3
  • are you sure this works? Looks like useEffect will only be called once! Commented Nov 8, 2019 at 22:01
  • 1
    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.
    – slinden
    Commented Nov 10, 2019 at 11:13
  • 1
    I used your solution and it works great for me. Thank you. +1 Commented Nov 12, 2019 at 16:43
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;
4
  • So using this, we don't need call initGA() on every component in componentDidMount() / componentWillMount() ?
    – TechSavy
    Commented Dec 3, 2018 at 7:46
  • 5
    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.
    – slinden
    Commented Sep 13, 2019 at 12:10
  • (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. Commented Nov 12, 2019 at 16:47
  • 1
    @slinden 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 all
    – tmatyo
    Commented Aug 2, 2020 at 9:56
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>
  )
}
1
  • 1
    This is the easiest implementation that works comprehensively at this point, thanks!
    – dgamboa
    Commented Mar 25, 2021 at 14:17
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

3
  • What do you mean "in your routing"? Can you give more detailed example with code?
    – Nastro
    Commented Oct 9, 2018 at 15:31
  • 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 manually
    – Nastro
    Commented Oct 9, 2018 at 16:15
  • 1
    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.initialize
    – Rolly
    Commented Jul 31, 2019 at 22:12
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.

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();
   }, []);
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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.