11

I am trying to use Google Map in React Component and it doesn't seem to work. I am currently referring to https://developers.google.com/maps/documentation/javascript/adding-a-google-map

Here is the code that I have for my component:

class ContactBody extends React.Component {
  componentWillMount() {
    const script = document.createElement("script");
    const API = 'AIzaSyDbAz1XXxDoKSU2nZXec89rcHPxgkvVoiw';
    script.src = `https://maps.googleapis.com/maps/api/js?key=${API}&callback=initMap`;
    script.async = true;

    document.body.appendChild(script);
  };

  initMap() {
    const uluru = {lat: -25.363, lng: 131.044};
    const map = new google.maps.Map(document.getElementById('map'), {
      zoom: 4,
      center: uluru
    });
    const marker = new google.maps.Marker({
      position: uluru,
      map: map
    });
  }

  render() {
    
    this.initMap();
    
    return (
      <div>
        <h1>Contact</h1>
        <div id="map" style={{width: 400, height: 300}}></div>
      </div>
    )
  }
}

ReactDOM.render(
  <ContactBody />,
  document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="react"></div>

However, when I run this, I am getting "Uncaught ReferenceError: google is not defined"

Could anyone tell me what is wrong with my code?

Thank you.

1
  • have you added the https://maps.googleapis.com/maps/api/js?key=${API}&callback=initMap; to your index.html file? Commented Jan 29, 2018 at 3:33

7 Answers 7

26

You're adding a <script> tag to your document to load the Google Maps API, but you aren't waiting for it to actually load before running your initMap method. Since it hasn't loaded yet, the google variable doesn't yet exist.

You've added a parameter to the script's URL, callback, with a value initMap. The Google Maps API will see this and run a function called initMap once it's ready. But your initMap method is not available from the global scope, and so will not be run.

One way to fix your code would be to make yourself a promise for the Google Maps API, and resolve that promise in a (global) callback function the Google Maps API can run. In your component code you'd then wait for the promise to be resolved before proceeding.

That might look something like this:

class ContactBody extends React.Component {
  getGoogleMaps() {
    // If we haven't already defined the promise, define it
    if (!this.googleMapsPromise) {
      this.googleMapsPromise = new Promise((resolve) => {
        // Add a global handler for when the API finishes loading
        window.resolveGoogleMapsPromise = () => {
          // Resolve the promise
          resolve(google);

          // Tidy up
          delete window.resolveGoogleMapsPromise;
        };

        // Load the Google Maps API
        const script = document.createElement("script");
        const API = 'AIzaSyDbAz1XXxDoKSU2nZXec89rcHPxgkvVoiw';
        script.src = `https://maps.googleapis.com/maps/api/js?key=${API}&callback=resolveGoogleMapsPromise`;
        script.async = true;
        document.body.appendChild(script);
      });
    }

    // Return a promise for the Google Maps API
    return this.googleMapsPromise;
  }

  componentWillMount() {
    // Start Google Maps API loading since we know we'll soon need it
    this.getGoogleMaps();
  }

  componentDidMount() {
    // Once the Google Maps API has finished loading, initialize the map
    this.getGoogleMaps().then((google) => {
      const uluru = {lat: -25.363, lng: 131.044};
      const map = new google.maps.Map(document.getElementById('map'), {
        zoom: 4,
        center: uluru
      });
      const marker = new google.maps.Marker({
        position: uluru,
        map: map
      });
    });
  }

  render() {
    return (
      <div>
        <h1>Contact</h1>
        <div id="map" style={{width: 400, height: 300}}></div>
      </div>
    )
  }
}

ReactDOM.render(
  <ContactBody />,
  document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="react"></div>

Sign up to request clarification or add additional context in comments.

10 Comments

I stumbled upon your code and tried it out as I too am facing issues loading Google Map as a component... It so happens that I get an error 'google' is not defined from the line resolve(google). Is there anything that I seem to be missing? @tremby
My example code above is working at least in my browser (click "run code snippet" and you should get a map with a marker). The error you're describing means that window.resolveGoogleMapsPromise method is running before it's supposed to. Look for an accidental call to it. Or post your code; otherwise I can't help you further.
This works perfectly after I added /* global google */ at the top of the code. Might be useful for someone using the create-react-app way in future. :)
OK, that sounds like your build chain had something to do with the issue.
Directives in comments like /* global google */ aren't part of ES6 as far as I know. Looks like something to hint to a linter.
|
9

This is how I did it:

import React from 'react';

export default class GoogleMap extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      mapIsReady: false,
    };
  }

  componentDidMount() {
    const ApiKey = 'XXXXXXXXXXXXXXXXXXXX';
    const script = document.createElement('script');
    script.src = `https://maps.googleapis.com/maps/api/js?key=${ApiKey}`;
    script.async = true;
    script.defer = true;
    script.addEventListener('load', () => {
      this.setState({ mapIsReady: true });
    });

    document.body.appendChild(script);
  }

  componentDidUpdate() {
    if (this.state.mapIsReady) {
      // Display the map
      this.map = new window.google.maps.Map(document.getElementById('map'), {
        center: {lat: -34.397, lng: 150.644},
        zoom: 12,
        mapTypeId: 'roadmap',
      });
      // You also can add markers on the map below
    }
  }

  render() {
    return (
      <div id="map" />
    );
  }
}

Comments

5

for those who are getting error google is not defined just above the error mentioned by eslint go ahead an define

const google = window.google;

Comments

2

Add script to head tag (my recommend is react-helmet) then equal window.initMap to your initialize map function because in script src define (callback=initMap") a global function named 'initMap'.

import React, { useEffect } from 'react';
import { Helmet } from 'react-helmet';

const Map = () => {
  useEffect(() => {
    window.initMap = () => {
      new google.maps.Map(document.getElementById('map'), {
        center: { lat: -34.397, lng: 150.644 },
        zoom: 8,
      });
    };
  }, []);
  return (
    <>
      <Helmet>
        <script src="https://maps.googleapis.com/maps/api/js key=?key=YOUR_API_KEY&callback=initMap" async defer />
      </Helmet>
      <div style={{ height: "100%" }} id="map"></div>
    </>
  );
};

If in server side rendered must check is in browser environment because window in undefined in node.js environment:

... lang-js
if (process.browser) { // check browser environment
  window.initMap = (): void => {
    new google.maps.Map(document.getElementById('map'), {
      center: { lat: -34.397, lng: 150.644 },
      zoom: 8,
    });
  };
}
...

Comments

1

In react apps,

use script tag in index.html load google maps api is NOT working, will give you error Access origin deny .....

The work around is load google map api in downstream module system, use useEffect() hook.

As example, I simple use app.js load api, you can use any component to do so. No need to be app.js

here is my working code:

                 import React, { useState, useEffect } from 'react';

                  function App() {

                       useEffect(() => {


                            const googleMapScript = document.createElement('script');
                            googleMapScript.src=`https://maps.googleapis.com/maps/api/js?key=${process.env.REACT_APP_GOOGLEMAPS_API_KEY}&libraries=places&callback=initMap`;
                            googleMapScript.async = true;
                            window.document.body.appendChild(googleMapScript);

                      }); 

    return (

Do not forget to put your google map api key in .env file, so you can refer it as

              process.env.REACT_APP_GOOGLEMAPS_API_KEY

.env file should put in the root folder, same place where your package.json located, so .env file should next to package.json file.

This is when .env file looks like:

              REACT_APP_GOOGLEMAPS_API_KEY=AIzajfU2J8m4vE 

Remember, you key must start as "REACT_APP_"

Comments

0

If you are including Google API Script in your index.html then there is no need to createElement in componentDidMount(). You can do something like this-

import React from 'react';
export default class GoogleMap extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      mapIsReady: false,
    };
  }

  componentDidMount() {
    //Load Map after DOM is loaded
    window.onload = this.setState({mapIsReady : true});
  }

  componentDidUpdate() {
    if (this.state.mapIsReady) {
      // Display the map
      this.map = new window.google.maps.Map(document.getElementById('map'), {
        center: {lat: -34.397, lng: 150.644},
        zoom: 12,
        mapTypeId: 'roadmap',
      });
      // You also can add markers on the map below
    }
  }

  render() {
    return (
      <div id="map"></div>
    );
  }
}

Comments

-2

Try Typing This on the top of the page (works for me ) - /* global google */

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.