React-google-maps: ScriptJSLoader google is not defined

Created on 27 Jan 2016  Â·  12Comments  Â·  Source: tomchentw/react-google-maps

Hi,

I'm trying to use the ScriptJSLoader but I run into an issue for which I don not see a workaround yet. Maybe you can help. My code is as follows:

 <ScriptjsLoader
        hostname={'maps.googleapis.com'}
        pathname={'/maps/api/js'}
        query={{v: '3', libraries: 'geometry,drawing,places'}}
        loadingElement={
          <div {...this.props} style={{ height: '100%' }}>
            <CircularProgress className='LocationPicker-loading' mode="indeterminate" color="white" size={0.5}/>
          </div>
        }
        containerElement={
          <div style={{height: '100%'}}/>
        }
        googleMapElement={
          <GoogleMap ref='map'
                     zoom={12}
                     defaultCenter={mapCenter}
                     onClick={::this.handleMapClick}>
            {marker}
            <DrawingManager
              circlecomplete={::this.handleCircle}
              defaultDrawingMode={google.maps.drawing.OverlayType.CIRCLE}
              defaultOptions={{
                drawingControl: true,
                drawingControlOptions: {
                  position: google.maps.ControlPosition.TOP_CENTER,
                  drawingModes: [
                    google.maps.drawing.OverlayType.CIRCLE
                  ]
                },
                circleOptions: {
                  fillColor: '#ff0000',
                  fillOpacity: 0.5,
                  strokeWeight: 2,
                  clickable: false,
                  editable: true,
                  zIndex: 1
                }
              }}
            />
          </GoogleMap>
        }
      />

But when I use this it results in:

Uncaught ReferenceError: google is not defined

this is logical off course because it is loaded asynchronously and I try to use the constants already. Does anybody see a workaroudn for this?

Most helpful comment

Using React Async Script Loader solved this issue for me. It exposes three props that you can use to determine whether the Google Maps API is loaded or not.

...
import scriptLoader from 'react-async-script-loader';

class Map extends Component {
  render() {
    const {isScriptLoadSucceed} = this.props;

    return (
      <div>
        {isScriptLoadSucceed && <GoogleMapLoader ... />}
      </div>
    )
  }
};

export default scriptLoader('//maps.googleapis.com...')(Map);

All 12 comments

+1

I just went out to jsfiddle, and popped in the following code:

<div id="map_div" style="width: 400px; height: 300px"></div>

<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?v=3.exp&libraries=drawing&sensor=false"></script>

<script type="text/javascript">
console.log(google.maps.drawing.OverlayType);
console.log(google.maps.ControlPosition);
</script>

Which outputs:

Object [OverlayType] {MARKER: "marker", POLYGON: "polygon", POLYLINE: "polyline", RECTANGLE: "rectangle", CIRCLE: "circle"}

Object [ControlPosition] {BOTTOM: 11, BOTTOM_CENTER: 11, BOTTOM_LEFT: 10, BOTTOM_RIGHT: 12, CENTER: 13, LEFT: 5, LEFT_BOTTOM: 6, LEFT_CENTER: 4, LEFT_TOP: 5, RIGHT: 7, RIGHT_BOTTOM: 9, RIGHT_CENTER: 8, RIGHT_TOP: 7, TOP: 2, TOP_CENTER: 2, TOP_LEFT: 1, TOP_RIGHT: 3}

Since the values required in the JSON object of the ScriptjsLoader are just JSON constants themselves, then you should be able to pass in their literal values, like so:

             drawingControlOptions: {
                  position: {2},
                  drawingModes: [
                    {"circle"}
                  ]
                },

and get what you need.

I figured out the position 1st, tested as a literal JSON object value, and it moved my control to top-center. The OverlayType objects _should_ work after adding their JSON literal values, but I didn't test it.

( I stumbled upon this "workaround" because I also had a need for the ScriptjsLoader b/c I was working on an React Server-side rendered app, and node.js was basically dropping my access-control-request-origin header & I had to find ALL the workarounds).

best of luck

Same issue here, no way to get a reference to google object with ScriptJSLoader.. Any workaround?

Gabriel,

As I stated in my comment, just grab the constant value that google maps returns in the console, and put that directly into the JSON key.

So, instead of

defaultDrawingMode={google.maps.drawing.OverlayType.CIRCLE}

(their constant)

You would just put

defaultDrawingMode={"CENTER"}

For the position, I posted in my comment all of the values that are valid. For example, instead of

position: google.maps.ControlPosition.TOP_CENTER

You just put

position: {2}

You can find all of their 'constant' values by playing around with jsfiddle as also noted in my comment. Just hit 'F12' to bring up dev tools, and check out the following jsfiddle to see what I mean. Expand the returned constants so you can pluck (map) whatever value you want.

http://jsfiddle.net/dwrtLcmu/

Wouldn't it be easier to do this:

let defaultDrawingMode, drawingControlOptions;

if(typeof google !== 'undefined'){
        defaultDrawingMode = google.maps.drawing.OverlayType.CIRCLE;
        drawingControlOptions = {
                  position: google.maps.ControlPosition.TOP_CENTER,
                  drawingModes: [
                          google.maps.drawing.OverlayType.CIRCLE
                  ]
        };
}

<ScriptjsLoader
        hostname={'maps.googleapis.com'}
        pathname={'/maps/api/js'}
        query={{v: '3', libraries: 'geometry,drawing,places'}}
        loadingElement={
          <div {...this.props} style={{ height: '100%' }}>
            <CircularProgress className='LocationPicker-loading' mode="indeterminate" color="white" size={0.5}/>
          </div>
        }
        containerElement={
          <div style={{height: '100%'}}/>
        }
        googleMapElement={
          <GoogleMap ref='map'
                     zoom={12}
                     defaultCenter={mapCenter}
                     onClick={::this.handleMapClick}>
            {marker}
            <DrawingManager
              circlecomplete={::this.handleCircle}
              defaultDrawingMode={defaultDrawingMode}
              defaultOptions={{
                drawingControl: true,
                drawingControlOptions: drawingControlOptions,
                circleOptions: {
                  fillColor: '#ff0000',
                  fillOpacity: 0.5,
                  strokeWeight: 2,
                  clickable: false,
                  editable: true,
                  zIndex: 1
                }
              }}
            />
          </GoogleMap>
        }
      />

If you choose to use asynchronous loading (i.e. with ScriptjsLoader), you definitely have to handle the case that google is not defined in the first render cycle. It's the consumer's responsibility to deal with it.

Maybe we need to expose callback attribute for user to setState later……

I am also struggling with this issue. Is there any error callback for ScriptjsLoader function, so that I can return if google.map is not available or taking too long to respond.

Feel free to submit a PR. Close for now

We're also looking for maintainers. Involve in #266 to help strengthen our community!

import {
  ReactScriptLoader,
  ReactScriptLoaderMixin,
} from 'react-script-loader';

export default class UiMap extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      scriptLoading: true,
      scriptLoadError: false,
    };
  }
  componentDidMount() {
    // eslint-disable-next-line
    ReactScriptLoader.componentDidMount(this.__getScriptLoaderID(), this, this.getScriptURL());
  }
  componentWillUnmount() {
    // eslint-disable-next-line
    ReactScriptLoader.componentWillUnmount(this.__getScriptLoaderID(), this.getScriptURL());
  }
  onScriptLoaded() {
    // ReactScriptLoader calls this function when the script has loaded.
    this.setState({ scriptLoading: false });
  }
  onScriptError() {
    // ReactScriptLoader calls this function when the script has failed to load.
    this.setState({ scriptLoading: false, scriptLoadError: true });
  }
  getScriptURL() {
    // this function tells ReactScriptLoaderMixin where to load the script from
    const k = 'xxxxxxxxxxxxx';
    return `https://maps.googleapis.com/maps/api/js?v=3.exp&key=${k}&libraries=geometry,drawing,places`;
  }
  __getScriptLoaderID() {
    return ReactScriptLoaderMixin.__getScriptLoaderID();
  }
  ...
}

Using React Async Script Loader solved this issue for me. It exposes three props that you can use to determine whether the Google Maps API is loaded or not.

...
import scriptLoader from 'react-async-script-loader';

class Map extends Component {
  render() {
    const {isScriptLoadSucceed} = this.props;

    return (
      <div>
        {isScriptLoadSucceed && <GoogleMapLoader ... />}
      </div>
    )
  }
};

export default scriptLoader('//maps.googleapis.com...')(Map);

Just want to let you knwo 6.0.0 is released on npm beta tag now. We also have a new demo page. Feel free to try it:
https://tomchentw.github.io/react-google-maps/

By using react-async-script-loader and _componentWillReceiveProps_ solved my problem. whatever I do with _google.maps_ that I can do it once the script is loaded successfully from react-async-script-loader.

Here is my sample code,

https://gist.github.com/sudharsan1988/73b36e23655ece48d0093adb3f84d5f9

Was this page helpful?
0 / 5 - 0 ratings

Related issues

wayofthefuture picture wayofthefuture  Â·  3Comments

ShintaroNippon picture ShintaroNippon  Â·  3Comments

timkraut picture timkraut  Â·  3Comments

LukasZvikas picture LukasZvikas  Â·  3Comments

bansalvks picture bansalvks  Â·  3Comments