Chris
Chris

Reputation: 1230

How to correctly integrate google API with React

I've integrated the Google analytics Embed API with my React app and I'm able to render correctly a graph. This is my container, which renders a line chart through the component UsersChart:

class StatsContainer extends Component {
    constructor(props) {
        super(props);
        initAnalyticsAPI();
    }

    render() {
      return (
         <Query query={GET_ACCESS_TOKEN}>
          {({ loading: loadingToken, error: errorToken, data: { getAnalyticsAccessToken } }) => (
              <Query query={GET_BASIC_STATS}>
                  {({ loading: loadingStats, error: errorStats, data: { getBasicStats } }) => {
                    if (loadingToken || loadingStats) return 'Loading...';
                    if (errorStats) return `Error! ${errorStats.message}`;
                    else if (errorToken) return `Error! ${errorToken.message}`;

                    const token = getAnalyticsAccessToken;

                    return (
                      <Fragment>
                        <div className="stats-container">
                          {/* ... */
                          <UsersCharts token={token} />
                        </div>
                      </Fragment>
                    );
                  }}
              </Query>
        )}
        </Query>
    );
  }
}

initAnalyticsAPI just appends the script to the document, using the official code:

function loadGA() {
   /* eslint-disable */
    (function(w,d,s,g,js,fs) {
        g = w.gapi || (w.gapi={});
        g.analytics = { q:[], ready: function(f) { this.q.push(f); }};
        js = d.createElement(s); fs = d.getElementsByTagName(s)[0];
        js.src='https://apis.google.com/js/platform.js';
        fs.parentNode.insertBefore(js,fs);
        js.onload = function() {
            g.load('analytics');
        };
      }(window, document, 'script'));
    /* eslint-enable */
}

export default function initialize() {
    if (typeof window === 'undefined') {
        return false;
    }

    // avoid downloading the library multiple times if it's already defined
    if (_.isEmpty(window.gapi)) {
        loadGA();
    }

    return window.gapi;
}

To keep it short, UsersCharts renders immediately the container used by the chart, while Google API will load it as soon as it's ready:

class UsersCharts extends Component {
    constructor(props) {
        super(props);
        this.chart = null;
    }

    componentWillUnmount() {
       // how to properly unmount it?
    }

    render() {
        const { token } = this.props;
        window.gapi.analytics.ready(() => {
            /**  Authorize the user with an access token obtained server side. */
            window.gapi.analytics.auth.authorize({
                serverAuth: {
                    access_token: token,
                },
            });

            this.chart = new window.gapi.analytics.googleCharts.DataChart({
            query: {
               ...
            },
            chart: {
                ...
            },
         });
        this.chart.execute();
      });

      return (
          <Fragment>
              <div id="chart-container" />
          </Fragment>
      );
    }
}

The issue is that sometimes I get the following error when I go to another section, which implies that the container for the chart doesn't exist anymore inside the app since another component is rendered. What could I do to properly unmount the component? I search for an API which could allow me to unregister the chart, or at least to catch the error but I wasn't able to find it. Thanks

enter image description here

Upvotes: 2

Views: 1965

Answers (1)

Chris
Chris

Reputation: 1230

Doing a refactoring of UsersChart by mapping the status of both the library and the component, I was able to get rid of all the warnings:

import React, { Component } from 'react';
import PropTypes from 'prop-types';

class UsersCharts extends Component {
    constructor(props) {
        super(props);

        this._isMounted = false;
        this.state = {
            ready: false,
        };
    }

    componentDidMount() {
        this._isMounted = true;
        window.gapi.analytics.ready(() => {
            console.log('Ready to do fireworks');
            if (this._isMounted) {
                 this.setState({ ready: true });
            }
        });
    }

    componentWillUnmount() {
        this._isMounted = false;
    }

    render() {
        const { token } = this.props;

       if (this.state.ready) {
        /**  auth and draw chart */

        this.chart.execute();
       }

       return <div id="chart-container" />;
   }
}

Upvotes: 2

Related Questions