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?
+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.
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
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.