Codementor Events

Integrating Google Maps in React Using refs

Published May 11, 2016Last updated Jan 18, 2017
Integrating Google Maps in React Using refs

The content of this tutorial is extracted from a React Live Class given by Thomas Tuts.


Introduction

React's ref provides an easy way to access DOM nodes and components, and to integrate third-party
libraries in our React components. Let's take a brief look at how we can use React's ref attribute to hook up our component with a basic Google
Maps integration. As an exercise, we'll render a map using Google Maps that starts on the Eiffel Tower,
and has a button that pans us all the way to the Arc de Triomphe. Simple stuff, but it should be enough
to get you up and running with using refs. Of course, feel free to modify things as you see fit!

Here's a demo of the end product:

We're going to write our code in ES2015, so some familiarity with its syntax is required. To avoid
having to set up a whole build system, we'll use Codepen instead.

What's a ref?

ref is a special attribute in React that you can use to store a reference to that component or DOM node.
You can then access that React component (or DOM node) and do things with it. Keep in mind
that when you attach a ref to a DOM node, you get access to that node, whereas a ref on
a React component, will get you a reference to the component instance instead.
The ref attribute takes
either a function or a string, which is passed in much like you would pass in a prop.

When you pass in a function, that function gets executed when the component is mounted and unmounted.
You can then do stuff with the component, or store it for later use.

When you pass in a string instead, you can access the component using this.refs.refname. To keep
things simple, we'll use the string method of accessing our ref.

Why you would use ref

Suppose you want to add a custom scrollbar to your fancy looking component. To do so, you might use
something like perfect-scrollbar. If you check out the documentation, you'll notice
that we need to pass it a DOM element so it can initialize and add extra DOM elements to provide the
custom scrollbar:

var container = document.getElementById('container');
Ps.initialize(container);

We can use a ref here to pass to Ps.initialize().

As mentioned before, you can also use ref to access a React component. You can then access that
component instance in its parent. For example, this could be handy to call a method on a child component to clear an input
field after the parent component has finished processing the passed data. You could also use state for this, but this solution
is a little cleaner.

Exercise: Google Maps

You can find the basic setup for the exercise on Codepen.

When you open the page,
you'll most likely get a Google Maps error. That's because we need to add our own API key first. Let's
get started!

Adding the Google API key

To get a key for the Google Maps JS API, follow these instructions. Once
you have your key, plug it into the Codepen exercise by clicking the cog icon next to JS.

In the modal, you'll see a list of all our externally loaded dependencies (React, ReactDOM and Google Maps JS API).
In the last one that says https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap,
replace YOUR_API_KEY with the one that you just generated.

Rendering the initial map

Now that our Google Maps has been hooked up with an API key, let's take it for a spin. To access the
DOM node to render our map into, we'll need to add a ref first:

// In the Map's render() method
return (
  <div ref="map" style={mapStyle} ref="map">I should be a map!</div>
);

Now that we can access our ref, we'll use the componentDidMount() lifecycle method to instantiate
the map:

componentDidMount() {
  this.map = new google.maps.Map(this.refs.map, {
    center: EIFFEL_TOWER_POSITION,
    zoom: 16
  });
}

Why do we use componentDidMount()? If we used componentWillMount(), we would not have access to
our refs yet, because at that point, our component doesn't exist yet, but it will. When it has
mounted (i.e. componentDidMount(), we do have access to the DOM representation of our component, and
we can access our ref. This stuff should not go into render() either, because render() can get
called multiple times in a component's lifecycle (recreating the map every time, losing your position).

Even more important: a render() method should be pure and free from any side effects! Side effects
are things like making an XHR request, modifying variables outside of the function's scope, writing
data, manipulating the DOM, and so on. Render should only be concerned with returning a representation
of the component based on its current props and state. That's all!

Panning to the Arc De Triomphe

Lastly, let's add a button that pans the map to the Arc De Triomphe. First, we'll add a button to
our component, along with a click handler:

return (
  <div>
    <button onClick={this.panToArcDeTriomphe}>Go to Arc De Triomphe</button>
    <div ref="map" style={mapStyle}>I should be a map!</div>
  </div>
);

You might have noticed that I am not returning not just the map <div>, but rather one surrounding
<div> that contains both the map and the button. This is because the render() method can only
return one child element, that optionally has children of its own.

To add the click handler logic, we first need to know how we can achieve panning in the Google Maps
JS API. We can use the Google Maps JS API Reference to find this out.
Luckily, there's a .panTo() method that takes a latLng pair, much like the one we used to instantiate
the map. Let's add the method to pan to the desired location:

panToArcDeTriomphe() {
  this.map.panTo(ARC_DE_TRIOMPHE_POSITION);
}

If you click the button, you'll notice that the map doesn't pan yet. That is because React ES2015
components do not autobind your methods for you. In the .panToArcDeTriomphe() method, we don't
have access to the right scope, and cannot access this.map. To fix this, you can use .bind() to
create a new function that does have the right scope. This can be done either when defining the click handler, or in
your constructor. I prefer placing the .bind() stuff in the constructor since it's a little cleaner:

constructor() {
  super();
  this.panToArcDeTriomphe = this.panToArcDeTriomphe.bind(this);
}

If you click the button now, it should pan your map to the right location. If you feel like you
missed something or have run into any errors, check out the solution on Codepen
if you need a nudge in the right direction.


Found this tutorial helpful? Be sure to sign up for Codementor's React Live Class where you can learn React with a live instructor! You can also find Thomas's original blog post here.

Discover and read more posts from Thomas Tuts
get started
post comments7Replies
Alireza Barkhordari
7 years ago

Thanks a lot.

Yungil Hong
7 years ago

This is prob the best way to use google maps api without a npm module. Thanks!

Gaurav Paliwal
7 years ago

Thanks, Thomas!

It helped a lot.

Show more replies