Decoupling from Google Maps
Adapted from a technical talk I gave at Frontend York about using the Google Maps JS badly on a website, the technical debt that can be accrued, and a few different modern techniques to use it well. Full code is available here.
When we rely on Google Maps for it’s API (I.e. geocoding addresses, Places API) it becomes a hard dependency on your JS code that becomes a legacy problem and hard to remove.
We’ve all seen and probably worked on single page apps that use Google Maps, ones where you have some data to display. These all load the Google Maps Javascript from the Google servers, take some of your data, and then show it on the maps, usually as markers.
You are relying on
- The internet connection between you and Google servers to work;
- Google’s servers to be nice and give you the Javascript;
- Your browser to download, parse and then execute that code and your application’s code.
How Google told you to use their code 2005 - 2015 (ish)
The problems with using this style of code in your large modern JS application might not be immediately clear, but…
- This is on the HTML page, and not with the rest of our JS code. This is outside of our (imaginary) webpack’ed application;
- The Google Maps JS is loaded syncronously - it waits for the page to finish parsing before it begins to download it;
- The
initMap
callback triggered on load by the Maps JS effectively becomes where the code execution starts from. Which is bad. We want our code to run independently of Google telling us to run it.
Let’s defer the script
To resolve the Google Maps JS being loaded syncronously and slowing down our page, we could defer the loading until after the page has loaded, or load it asyncronously so that our page doesn’t wait for the JS to complete it’s download before rendering.
But now we’ve made things even worse -
- The JS isn’t loading in a predictable order. Any other JS files that interact with our map wont work
window.google.maps
is not available when the page first loadsinitMap
function becomes the entry point for our entire app. Everything in our app has to be done in here
Because window.google.maps
isn’t immediately available to us on page load, we need to use callback initMap
- that the Maps JS runs once it has completed downloading and is ready. All our code then has to be loaded from that callback in order to work properly.
This is coupling
Our code is now tightly coupled to the window.google.maps
variable and permanently relies on the Google Maps JS on running our callback. We have an interdependence on the Maps JS library, one that will remain until we address the underlying problems I’ve identified here.
But does it matter?
Modern large apps that display data on a Map have lots of components, lots of moving parts. If we rely on Google’s JS in this way (or, indeed any third party library) then Google Maps is our app, and not just a component that we can use.
We’ll find that other components that interact with our map will rely on window.google
and not work without it, so we’ll write them for the Google Maps JS, rather than us just using it as an external library.
So what can we do about it?
Vanilla HTML component
We can use modern ES6 promises to await the Google JS before displaying the map using a custom parameter.
And then we load it with
Now we’re
- Async loading the script, only once (which we were doing anyway)
google.maps
is encapsulated within thegmap
function, we’re not using it elsewhere so we can’t get errors about it not existing- We can fully control when the map is loaded - I’ve used an event listener for DCL, but you could add it for button presses, or on search results being shown dynamically for instance
W3C Web Component
W3C started to standardise this idea in 2014, and have a proposed standard for Web Components into reusable and modular components. It means we can combine vanilla JS, HTML & CSS and instantiate it through custom HTML tags -
1
2
class Map extends HTMLElement {...}
window.customElements.define('map', Map);
Then we can use our new custom HTML tag for our component
1
<map>...</map>
As the standard isn’t fully drafted yet, I won’t go much into this, other than it’s a thing. See more about it here
React component
react-google-maps
is a mature React component that’s ready to use and comes ready componentised. It also allows for
- Compiled scripts handling the markers and async loading - we’re not re-writing them
- The data for our markers is out of the DOM entirely, and within the state system
- The Map class is a component and can be reused in the same way
- Interactions with the Map class becoming components in their own right, allowing for seperating out user actions from the logic, for example