js-api-loader
js-api-loader copied to clipboard
Loader reports warning when using importLibrary multiple times
When loading the maps library followed by another library using loader.importLibrary() and async/await syntax, a misleading warning-message "Google Maps already loaded outside @googlemaps/js-api-loader.This may result in undesirable behavior as options and script parameters may not match." is written to the console.
Here's the code that triggers this behavior:
import Loader from '@googlemaps/js-api-loader';
async function main() {
const loader = new Loader({apiKey: 'MY_API_KEY'});
const {Map} = await loader.importLibrary('maps');
// this line will trigger the warning message:
await loader.importLibrary('marker');
}
The reason appears to be that the done flag isn't correctly set to true after the maps-api has initially been loaded.
Any updates on this issue, I have been facing the same issue for a while now.
Also, could be related to this:
seems like even if we specify a particular library name, it still returns the whole payload i.e. having the following
await loader.importLibrary('marker');
still loads and populates every library, as if we had called the deprecated .load() method.
FWIW I found this example of loading multiple libraries here: https://cloud.google.com/blog/products/maps-platform/more-control-loading-maps-javascript-api
Hi @usefulthink,
The assistance from @venatiodecorus was incredibly useful in this context.
Regarding the implementation, you can leverage Promise.all to efficiently load your libraries concurrently. Here's how you could do it:
const [mapLibrary, markerLibrary] = await Promise.all([
loader.importLibrary('maps'),
loader.importLibrary('marker')
]);
This code will allow you to import both maps and marker libraries in parallel, which can help streamline your setup process.
Best regards!
This bug is related to this line https://github.com/googlemaps/js-api-loader/blob/main/src/index.ts#L630
Threre is no check if specific library is loaded, this check is uselles if libraries are imported one by one.
Fix: Promise.all() instead await for every importLibrary.
import { Loader } from '@googlemaps/js-api-loader';
const loader = new Loader({
apiKey: ''
version: 'weekly',
});
let libraries: any = null;
async function loadLibraries() {
if (!libraries) {
// not necessary to load all but just for an example.
libraries = await Promise.all([
loader.importLibrary('maps'),
loader.importLibrary('places'),
loader.importLibrary('marker'),
loader.importLibrary('geocoding'),
]);
}
return libraries;
}
const [{ Map }, { Autocomplete }, { AdvancedMarkerElement }, { Geocoder }] = await loadLibraries();
export { Map, AdvancedMarkerElement, Geocoder, Autocomplete };
So this is an interesting race condition bug that I think only happens with two back-to-back await loader.importLibrary... calls.
Even putting an extra await 0; between the two calls resolves the issue. https://jsfiddle.net/b6vsx8dp/1/
(Not that I'd recommended this, just diagnosing.)
Though for what it's worth, even after this bug is fixed I would recommend using Promise.all() [edit: instead of back-to-back await loader.importLibrary...], as that allows the libraries to load in parallel.
Promise.all will not be helpful if you load libraries on need.
const { Map } = await loader.importLibrary("maps");
const showPlaces = async (places) => {
const { Autocomplete } = await loader.importLibrary("places");
// ...
};
@Profesor08 Yes, that's true.
You will need to call Promise.all() first and then access the variables later.
Something like this.
const [{ Map },{Autocomplete}] = await Promise.all([
loader.importLibrary("maps"),
loader.importLibrary("places")
]);
const showPlaces = async (places) => {
// you can access `Autocomplete` variable here.
// Also you can access them using google.maps.places.Autocomplete
};
Maybe in future we could pass an array of Libraries instead 1 library in importLibrary(), but for now use Promise.all().
You can use Promise.all() in your main.js file and in other files you can access the map Libraries normally as you do using google.maps.
Or if using react then you can simply define them in a Context and share the libraries as props to nested components.