1

I am trying to add the google api script programmatically when it is required. However, I get an error that google is not defined. I can see that the script is added in before the end of the body tag.

Earlier I had the script loaded in the index.html file however, I have created a different component elsewhere in the app now which require its own script as it has a different api key. Therefore, I had to remove the script from the index.html as it was giving an exception for multiple use of the script. Now I would like to add it when it is the component is loading.

Please refer the code below for the main component:

import React from 'react';
import { Button } from 'reactstrap';
import CitySuggestionBar from './CitySuggestionBar';

export default class Destination extends React.Component{

    componentDidMount(){
        this.renderScript();
    }

    renderScript = () => {
        loadScript('https://maps.googleapis.com/maps/api/js?key=MY_API_KEY&libraries=places');
      }

    showPlaceDetails(place) {
        let city = place.address_components[0].long_name.toString();
        try{
            city+= '+' + place.address_components[2].long_name.toString();
        }catch(e){}
        city = city.replace(/\s/g, "+");
        sessionStorage.setItem('city', city);
        console.log(city);
    }

    redirect = () =>{
        sessionStorage.getItem('city') ? this.props.history.push("/hotels") : alert('Please select a city first');
    }

    render(){
        return(
            <div className="location-search-container">
                <div className="location-search-wrapper">
                    <h1>Search for a city...</h1>
                    <CitySuggestionBar onPlaceChanged={this.showPlaceDetails.bind(this)} />
                    <Button onClick={this.redirect} className="btns" to="/hotels" color="primary">Proceed</Button>
                </div>
            </div>
        );
    }
}

const loadScript = (url) => {
    const index = window.document.getElementsByTagName('script')[0];
    const script = window.document.createElement('script');
    script.src=url;
    index.parentNode.insertBefore(script, index);

  }

Below is the code for the component where the google map is being used and it is a sub component of the above main component:

import React from "react";
/* global google */


export default class CitySuggestionBar extends React.Component {
  constructor(props) {
    super(props);
    this.autocompleteInput = React.createRef();
    this.autocomplete = null;
    this.handlePlaceChanged = this.handlePlaceChanged.bind(this);
  }

  componentDidMount() {
    this.autocomplete = new window.google.maps.places.Autocomplete(this.autocompleteInput.current,
        {"types": ['(cities)']});

    this.autocomplete.addListener('place_changed', this.handlePlaceChanged);
  }



  handlePlaceChanged(){
    const place = this.autocomplete.getPlace();
    this.props.onPlaceChanged(place);
  }



  render() {
    return (
        <input ref={this.autocompleteInput}  id="autocomplete" placeholder="Search"
         type="text"></input>
    );
  }
}

Please Help! Thanks in advance.

rajput sufiyaan
  • 61
  • 1
  • 14

1 Answers1

0

In the above snippet, I can see that every time the componentDidMount it will again create another script tag to avoid this, you can modify the loadScript methods as follows:

const loadScript = (url) => {
    const googleScript = window.document.getElementByClassName('google-script');
    if (googleScript.length === 0) {
        const script = window.document.createElement('script');
        script.src=url;
        script.class="google-script"
        document.body.appendChild(script)
    }
}

If you like to remove the google script you can handle this inside componentWillUnmount.

Using this will not show you an exception for multiple uses of the script tag.

Also if you like to know that the script tag is loaded or not you can find it by adding another like in loadScript method as follows:

const loadScript = (url) => {
    const googleScript = window.document.getElementByClassName('google-script');
    if (googleScript.length === 0) {
        const script = window.document.createElement('script');
        script.src=url;
        script.class="google-script"
        document.body.appendChild(script)
        script.onload = () => {
            // Place code here to do further action.
        };
    }
}

<----------------------------Update--------------------------->

In order to resolve "google is undefined" error you can try following the approach where you create 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.

const loadScript = () => {
  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 = //your api key;
        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.loadScript();
}

componentDidMount() {
  // Once the Google Maps API has finished loading, initialize the map
  this.getGoogleMaps().then((google) => {
    const uluru = { lat: -25.366, 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>
      <div id="map" style={{width: 600, height: 300}}></div>
    </div>
  )
}
Piyush Zalani
  • 3,686
  • 1
  • 13
  • 29
  • I don't get an error about multiple uses it was earlier when I placed the script tag manually in the index.html. The error that I get now is google not defined. Your suggestion was helpful for removing the script when it will unmount. But, First it appears that google is undefined even when I see the script tag is created by my code above. – rajput sufiyaan Dec 28 '18 at 14:57
  • @rajputsufiyaan, I have updated the answer, please have a try if that helps – Piyush Zalani Dec 28 '18 at 17:13