How to load the google maps api <script> in my react app only when it is required?
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);
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
https://usehooks.com/useScript/
//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>
);
}
Yet another package react-dependent-script
package to load Google Maps libraries, which provides the following features:
- ensure to load the JavaScript and/or CSS first, then render your content
- ensures the external content is only loaded once, regardless of how many times the render() function is called
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