React-google-maps: How to add Custom Controls?

Created on 10 Apr 2018  Â·  18Comments  Â·  Source: tomchentw/react-google-maps

Hi, I am using react-google maps, its great. I am showing multiple markers in the map Now I want a custom control i.e a drop down inside the map, in which If I can select a particular marker and it will only show that marker.

The only problem I am facing is I am unable to show that drop down on the map.

Is there any way to show it on the map?

Any help will be appreciated.

Most helpful comment

All 18 comments

@tomchentw Is there anything which can be done for this?
Can we create our own custom control just like SearchBox component?

@SyedSaifAli We can create custom controls to render on the map,

  1. First we need to create a div and add it into Google Maps Controls, something like below

this.map.controls[google.maps.ControlPosition.BOTTOM_RIGHT].push( showhideControlDiv //div container );

  1. Now you can render any component into that Div, with
    ReactDom.render(<ComponentName/>, document.getElementById("showhideControlDiv "))

In the above way we are able to show dropdowns and legends on the Map

@RamYadlapalli Can you provide an example of how to implement it?

This is not totally related to this post, but I am having issues where I need to remove a custom control from google map object.

did a repo for this.
https://github.com/github0013/react-google-maps-custom-control-removal

@jgimbel
https://gist.github.com/jgimbel/6a36d60e28aaf453d0093ddc47f36533#file-mapcontrol-js-L15
This line tries to remove the pushed element, and I am assuming this index is from the push return value. "this.divIndex" assignment is missing from the gist.

I did like this
https://github.com/github0013/react-google-maps-custom-control-removal/blob/master/src/MapControl.tsx#L20

But I am not sure if it's right since

https://developers.google.com/maps/documentation/javascript/reference/3.exp/map
this.map.controls[this.props.position] is MVCArray
maps_ _ _google_maps_javascript_api_ _ _google_developers

https://developers.google.com/maps/documentation/javascript/reference/3.exp/event#MVCObject
and the return value from the push method is
event_system_ _ _google_maps_javascript_api_ _ _google_developers
It's only the length

Problem

My problem is when to actually unmount the component
https://github.com/github0013/react-google-maps-custom-control-removal/blob/master/src/MapContainer.tsx#L21

this error hapens.
0e02ec913ef91e384b8a79e241dd04b1

so how did you actuall get this.divIndex value, and remove the control element safely?

So...
I am not unmounting my custom controls, so I wrote it initially without the removeAt. Then I remembered that when I wrote custom controls for leaflet I was informed that the controls were not getting removed on unmount. So I added unmounting to the gist and I forgot to set divIndex like you mentioned. Long story short, now that react 16 has portals unmounting is less necessary than it was back when I wrote this kind of thing for leaflet.
Without calling removeAt there is a small memory leak because the mounting div is never cleaned up. If the returned value of push is the length of the array, it sounds like you can just subtract one from it to get the correct index. Haven't tested that though.

This is how I did, a little adjust base on @jgimbel 's example.

Map.js

class Map extends React.Component {
  componentDidMount() {
    // add <CustomControl /> to mapControl
    this.map.context[MAP].controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(this.control);
  }

   render() {
     return (
        <GoogleMap ref={(ref) => { this.map = ref; }}>
            <CustomControl  ref={(ref) => { this.control = ref; }} />
             ...
        </GoogleMap>
     )
   }
}

CustomControl.js

export default () => (
  <div>
    // something want to display on control
  </div>
)

@jgimbel @SyedSaifAli I was getting the same TypeError while unmounting the the map controls.
this is how I fixed it :
https://gist.github.com/sakhisheikh/87dd587ec126189e95396c380d49298f

@jgimbel I have updated the gist. You do a PR now.

I just did this using the constructor method instead of componentWillMount because it is unsafe now and struggled a little to make it work because I was not passing the context to the super call:

constructor(props, context) {
  super(props, context);
  const { controlPosition } = props;

  this.map = this.context[MAP];
  this.control = document.createElement('div');
  this.map.controls[controlPosition].push(this.control);
}

probably it is something basic, but better leave it here for future visitors.

So I ran into an error when unmounting multiple custom controls with the solution above. removeAt will make this.divIndex of other custom controls component no longer correct.
Here is how I fixed it for future visitors and future me:

componentWillUnmount() {
    const controlArray = this.map.controls[this.props.position].getArray()
    for (let index in controlArray) {
        if (controlArray[index] === this.controlDiv) {
            this.map.controls[this.props.position].removeAt(index);
            break;
        }
    }
}

@markmssd this library is unmaintained about a year, please look at ‘react-google-maps-api’ as replacement

@JustFly1984 Can you add link of that library? react-google-maps-api

@JustFly1984 Hey, Did you add some changes around DrawingManager in your forked library? Or do you of anyone else who managed to achieve DrawingManager public API's which are not exposed in this package?

For any other confused travelers, react-google-maps uses React's Legacy Context API instead of the current one. This was relevant for me because I was trying to get this done in a functional component. My completed component (using React 16.8 Hooks, but using the deprecated-in-16.3 Legacy Context) looks like this:

import React, { useEffect } from 'react';
import { createPortal } from 'react-dom';
import { MAP } from 'react-google-maps/lib/constants';
import PropTypes from 'prop-types';

export default function CustomDrawingManagerControl(
  { position = window.google.maps.ControlPosition.TOP_LEFT, children },
  context
) {
  const map = context[MAP];

  const controlDiv = document.createElement('div');

  useEffect(() => {
    const controls = map.controls[position];
    const index = controls.length;
    controls.push(controlDiv);
    return () => {
      controls.removeAt(index);
    };
  });

  return createPortal(
    <div style={{ marginLeft: 16, marginTop: 16 }}>{children}</div>,
    controlDiv
  );
}

CustomDrawingManagerControl.contextTypes = {
  [MAP]: PropTypes.object,
};

To be used like this:

<GoogleMap {...props}>
    <CustomMapControl position={google.maps.ControlPosition.BOTTOM_CENTER}>
      <div>That was easy</div>
    </CustomMapControl>
</GoogleMap>

@a8t I would advise you to look at @react-google-maps/api. We have rewritten it to typescript and new context API, as well as hooks API. https://github.com/JustFly1984/react-google-maps-api/tree/master/packages/react-google-maps-api

Was this page helpful?
0 / 5 - 0 ratings

Related issues

wayofthefuture picture wayofthefuture  Â·  3Comments

LukasZvikas picture LukasZvikas  Â·  3Comments

manoj150283 picture manoj150283  Â·  3Comments

tahir-masood1 picture tahir-masood1  Â·  4Comments

EvHaus picture EvHaus  Â·  3Comments