Reputation: 8851
I want to try and use the google maps API to show a map but I'm wondering if there's a better way to load the <script>
tag than putting it in my index.html.
I would like for the script to load only when I go to the /map
route. So, I would want to remove it from my index.html
and load it dynamically. However, I also want to make sure that if its already been loaded that I don't try and load it again.
I'm not sure if there is a library to handle this. What I've tried so far (but failed) is to create a loadScript
function which appends a <script>
to the actual dom and assigns it a key so in this case 'google-maps
.
Thanks
Upvotes: 13
Views: 23046
Reputation: 3232
As of April 2021 Google has "made it easier to use the Maps JavaScript API in modern web apps" more details on their blog post: Loading Google Maps Platform JavaScript in Modern Web Applications.
This are the instructions for the Google Maps JavaScript API Loader library
import { Loader } from '@googlemaps/js-api-loader';
const loader = new Loader({
apiKey: "",
version: "weekly",
libraries: ["places"]
});
const mapOptions = {
center: {
lat: 0,
lng: 0
},
zoom: 4
};
// Promise
loader
.load()
.then((google) => {
new google.maps.Map(document.getElementById("map"), mapOptions);
})
.catch(e => {
// do something
});
Or if you want, you can use the Google Maps JavaScript API React Wrapper library.
Upvotes: 6
Reputation: 5352
Updates Oct 6, 2019: The example code is still working well, I have just updated them to use non-decorator syntax.
This is what I make it worked in my recent project. I used react-async-script-loader component.
import React from 'react';
import scriptLoader from 'react-async-script-loader';
class Maps extends React.Component {
constructor(props) {
super(props);
this.map = null;
}
componentWillReceiveProps({ isScriptLoaded, isScriptLoadSucceed }) {
if (isScriptLoaded && !this.props.isScriptLoaded) {
// load finished
if (isScriptLoadSucceed) {
this.map = new google.maps.Map(this.refs.map, {
center: { lat: 10.794234, lng: 106.706541 },
zoom: 20
});
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
position => {
const pos = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
this.map.setCenter(pos);
const marker = new google.maps.Marker({
position: pos,
map: this.map,
title: 'Hello World!'
});
},
() => {
console.log('navigator disabled');
}
);
} else {
// Browser doesn't support Geolocation
console.log('navigator disabled');
}
} else this.props.onError();
}
}
render() {
return (
<div>
<div ref="map" style={{ height: '80%', width: '100%' }}></div>
{!this.map && <div className="center-md">Loading...</div>}
</div>
);
}
}
export default scriptLoader(['https://maps.googleapis.com/maps/api/js?key=API_KEY'])(Maps);
Upvotes: 17
Reputation: 964
Thanks Nguyen Thanh. As google
now in global scope I used window.google
.
import React, { Component } from 'react';
import scriptLoader from 'react-async-script-loader';
class Map extends Component{
constructor(props) {
super(props);
}
componentWillReceiveProps({isScriptLoadSucceed}){
if (isScriptLoadSucceed) {
var markers = [];
var map = new window.google.maps.Map(document.getElementById('map'), {
zoom: 12,
center: {lat: 37.7749300, lng: -122.4194200}
});
}
else{
alert("script not loaded")
}
}
render(){
return(
<div>
<div id="map" style={{height: "600px"}}></div>
</div>
)
}
}
export default scriptLoader(
["https://maps.googleapis.com/maps/api/js?key= APIKEY"]
)(Map)
With react hook we can also load external script
//useScript custom hooks from the site
let cachedScripts = [];
function useScript(src) {
// Keeping track of script loaded and error state
const [state, setState] = useState({
loaded: false,
error: false
});
useEffect(
() => {
// If cachedScripts array already includes src that means another instance ...
// ... of this hook already loaded this script, so no need to load again.
if (cachedScripts.includes(src)) {
setState({
loaded: true,
error: false
});
} else {
cachedScripts.push(src);
// Create script
let script = document.createElement("script");
script.src = src;
script.async = true;
// Script event listener callbacks for load and error
const onScriptLoad = () => {
setState({
loaded: true,
error: false
});
};
const onScriptError = () => {
// Remove from cachedScripts we can try loading again
const index = cachedScripts.indexOf(src);
if (index >= 0) cachedScripts.splice(index, 1);
script.remove();
setState({
loaded: true,
error: true
});
};
script.addEventListener("load", onScriptLoad);
script.addEventListener("error", onScriptError);
// Add script to document body
document.body.appendChild(script);
// Remove event listeners on cleanup
return () => {
script.removeEventListener("load", onScriptLoad);
script.removeEventListener("error", onScriptError);
};
}
},
[src] // Only re-run effect if script src changes
);
return [state.loaded, state.error];
}
Usage
//App.js
import React from "react";
import ReactDOM from "react-dom";
import { useState, useEffect } from "react";
function App() {
const [loaded, error] = useScript(
"https://maps.googleapis.com/maps/api/js?key=API_KEY"
);
useEffect(() => {
if (loaded) {
new window.google.maps.Map(document.getElementById("map"), {
zoom: 12,
center: { lat: 37.77493, lng: -122.41942 }
});
}
}, [loaded]);
return (
<div>
<div>
Script loaded: <b>{loaded.toString()}</b>
</div>
<div id="map" style={{ height: "600px" }} />
</div>
);
}
Upvotes: 12
Reputation: 59378
Yet another package react-dependent-script
package to load Google Maps libraries, which provides the following features:
Here is a simple example, where you wrap your map component in ReactDependentScript
component:
<ReactDependentScript
scripts={[
"https://maps.googleapis.com/maps/api/js?key={YOUR-KEY-GOES-HERE}"
]}>
<Map center={{ lat: -34.397, lng: 150.644 }} zoom={3} />
</ReactDependentScript>
Here is a demo for your reference
Upvotes: 3