Drawing google map and pins using React.Context
Tags: react, context, renderprop
Published: 2019-09-21 13:41:31



---
title: Drawing google map and pins using React.Context
published: true
description: Rendering pins on google map. An implementation React and Context api.
cover_image: https://thepracticaldev.s3.amazonaws.com/i/k3c47fp611b4l03g979b.jpg
tags: reactjs, context, render-prop
---

# The goal
What I envisioned is a react way of rendering the map and drawing items _(pins, popups etc..)_ on the map.
```jsx
<Map>
  {// Marker component that renders a pin icon on the map }
  <Marker lat={lat1} lng={lng1} />
  <Marker lat={lat2} lng={lng2} />
</Map>
```

{% codepen https://codepen.io/rihdus-1470886313/pen/qBWMrKg %}

## Step 1: Create Basic components to encapsulate google maps api
Google [Map](https://developers.google.com/maps/documentation/javascript/reference/map#Map.constructor) class renders a new map in a given DOM element and the corresponding map instance provides apis to interact with the map. Other classes like [Marker](https://developers.google.com/maps/documentation/javascript/reference/marker#Marker.constructor) , [InfoWindow](https://developers.google.com/maps/documentation/javascript/reference/info-window#InfoWindow.constructor) and [Overlay](https://developers.google.com/maps/documentation/javascript/reference/overlay-view#OverlayView) allow your to draw custom UI on the map.

### Map component
Here is a basic map component to render a map in a given container.
```jsx
class Map extends React.Component {
   /** Map instance */
   map = null;
   /** DOM container where the map canvas gets rendered. */
   mapContainer = React.createRef();
   componentDidMount() {
     /** Create new google map. */
      this.map = new google.maps.Map(this.mapContainer.current, {
        zoom: this.props.zoom,
        center: this.props.center
      })
   }
   render() {
      return <div ref={this.mapContainer}
        style={{ height: '100vh', width: '100vw'}}></div>
   }
}
ReactDOM.render(<Map />, document.getElementById('root'))
```
###  Marker component
 Draw the marker on a given map at a given position. 
To draw a marker, we need the `map` object which is rendered on the DOM and pair or `lat`, `lng` values to position the marker on the map.
```jsx
class Marker extends React.Component {
   componentWillUnmount() {
      this.marker.setMap(null); // Remove the marker from the map
   }
   render() { 
     const { map, lat, lng } = this.prop
     // Create new marker and render it on the map.
     this.marker =  new Marker({ 
       map: map, // the map instance
       position:  { lat, lng } // position of the marker on the map
     });
     return null; 
   }
}
```
_For details, refer example usage for [Adding a Map with a Marker](https://developers.google.com/maps/documentation/javascript/adding-a-google-map) provided by Google._

## Step 2: Render Marker as a child component in the Map
Lets look at our target again..
```jsx
ReactDOM.render(<>
    <Map>
	  <Marker lat={lat1} lng={lng1} />
	</Map>
  </>, document.getElementById('root'))
```

The Marker component needs access to the `map` instance, which is created in the `componentDidMount` function in the `Map` component defined earlier. 
The `Map` component can pass down the map instance via [Render Props](https://reactjs.org/docs/render-props.html) or using [React.createContext](https://reactjs.org/docs/context.html#reactcreatecontext).


### Child Marker using React Context.
React Context can be used to send props from the parent `Map` component to child `Marker` component. 
Let's first create a Context for the map instance using the [createContext](https://reactjs.org/docs/context.html#reactcreatecontext) api. 
> The Map will provide the value of the context using the `<MapContext.Provider>` component. 
> _See [Context.Provider](https://reactjs.org/docs/context.html#contextprovider) component api._
```jsx
// Map context with default value of the map set to `null`.
const MapContext = React.createContext({ map: null })
...
class Map extends React.Component {
  render() {
  /**
  * Provide `map` value in map context. This value can be consumed 
  * in any child component using `<MapContext.Consumer>` component.
  */
    return <MapContext.Provider value={{map: this.map}} >
      {this.props.children}
    </MapContext.Provider>
  }
}
```

> In the Marker component we can access the map instance using `MapContext.Consumer` component.
> _See [Context.Consumer](https://reactjs.org/docs/context.html#contextconsumer) component api_
```jsx
class Marker extends React.Component() {
  /**
  * In the render function, we can use `<MapContext.Consumer>` component 
  * to receive the `map` received from parent `Map` component.
  */  
  render() {
    return <MapContext.Consumer>{({map}) => {
      const { lat, lng } = this.props
      // Create new marker and render it on the map.
      this.marker = this.marker || new Marker({ 
        map: this.map, // the map instance
        position:  { lat, lng }
      });
      this.marker.setPosition({ lat, lng })
      return null;
    }}</MapContext.Consumer>
  }
}
```
## Done!
```jsx
// Resulting JSX for rendering Marker on Maps.
ReactDOM.render(<>
    <Map>
      <Marker lat={lat1} lng={lng1} />
    </Map>
  </>, document.getElementById('root'))

```
To recap,
- We created a Map component that renders the _map canvas_, and provides the instance of the corresponding __map__ object to the children, using the __Provider__ component in the __React.Context__ api.
- We use the corresponding __Consumer__ component to retrieve the __map__ instance in the Marker component, to draw pins in the _map canvas_.

Another approach is to use Render Prop technique to provide map object instance to the child Marker component.
The `<MapContext.Consumer />` in `Marker` Component uses this render prop technique to provide access to the `map` instance.

#### A sample  implementation of Child Marker using Render Prop.
```jsx
class Map extends React.Component {
  render() {
    return this.props.children(this.map)
  }
}
// Render prop usage
ReactDOM.render(<>
    <Map>{map => {
      <Marker map={map} lat={lat1} lng={lng1} />
    }}</Map>
  </>, document.getElementById('root'))
```

React.Context is just one way to pass around data in React, there are other techniques more suitable for other use-cases. During this exercise, and others in the past, I did find some benefits of React.Context
- resulting JSX is much cleaner
- data provided by parent component can be accessed by any child component, at any depth, without requiring [explicit prop passing](https://twitter.com/acdlite/status/955955121979969537?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed&ref_url=https%3A%2F%2Fblog.logrocket.com%2Freact-custom-hooks-and-the-death-of-render-props-a0ce5cba387f%2F).

Thanks for reading my first tech write up.
Welcome any and all feedbacks.
Cheers.