Skip to content

Instantly share code, notes, and snippets.

@jgimbel
Last active March 23, 2024 08:08
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save jgimbel/6a36d60e28aaf453d0093ddc47f36533 to your computer and use it in GitHub Desktop.
Save jgimbel/6a36d60e28aaf453d0093ddc47f36533 to your computer and use it in GitHub Desktop.
//Example Usage of custom control
export default ({lat, lng, google}) => (
<GoogleMap
defaultCenter={{lat, lng}}
defaultZoom={14}
minZoom={6}
google={google}
containerStyle={{ height: '575px', width: '100%', position: 'relative' }}
>
<Marker position={{lat, lng}} />
<MapControl position={google.maps.ControlPosition.BOTTOM_CENTER}>
<div>That was easy</div>
</MapControl>
</GoogleMap>
)
import { Component } from 'react'
import { createPortal } from 'react-dom'
import { MAP } from 'react-google-maps/lib/constants'
import PropTypes from 'prop-types'
export default class MapControl extends Component {
static contextTypes = { [MAP]: PropTypes.object }
componentWillMount() {
this.map = this.context[MAP]
this.controlDiv = document.createElement('div')
this.map.controls[this.props.position].push(this.controlDiv)
}
componentWillUnmount() {
this.map.controls[this.props.position].removeAt(this.divIndex)
}
render() {
return createPortal(this.props.children, this.controlDiv)
}
}
@SyedSaifAli
Copy link

What should be the alternate for createPortal() if using older version of react , react-dom

@jgimbel
Copy link
Author

jgimbel commented Apr 23, 2018

In 15 use ReactDOM.unstable_renderSubtreeIntoContainer
In <=14 use ReactDOM.render

@SyedSaifAli
Copy link

getting this error while trying to use the above code using ReacDOM.render

_renderNewRootComponent(): Render methods should be a pure function of props and state; triggering nested component updates from render is not allowed. If necessary, trigger nested updates in componentDidUpdate.

@jgimbel
Copy link
Author

jgimbel commented Apr 23, 2018

See https://github.com/tomchentw/react-google-maps/blob/master/src/components/InfoWindow.jsx#L142 for example use of ReactDOM.unstable_renderSubtreeIntoContainer

@ruimigueldp
Copy link

Hi all, I'm trying to implement this example but I cant understand the google prop on Map.js. Can anyone explain me?

Thank you in advance

@MychaelZ
Copy link

MychaelZ commented Sep 4, 2018

In this example this.divIndex is undefined which throws an error on componentWillUnmount. This should fix it.

componentWillMount() {
    this.map = this.context[MAP];
    this.controlDiv = document.createElement('div');
    this.divIndex = this.map.controls[this.props.position].length;
    this.map.controls[this.props.position].push(this.controlDiv);
  }

@tranlehaiquan
Copy link

tranlehaiquan commented Nov 22, 2018

Because componentWillMount isn't recommend for new version. I move it to constructor, leave it here for anyone need.

import { Component } from 'react';
import { createPortal } from 'react-dom';
import { MAP } from 'react-google-maps/lib/constants';
import { string, element, object, oneOfType, array } from 'prop-types';

/**
 * This Component for add custom control to map
 * (map.controls[position].push(component))
 * NOTE:
 * Can ref to map through context in constructor (or this.context expect contructor)
 * User constructor to add div and render will createPortal
 */
export default class MapControl extends Component {
  static propTypes = {
    position: string.isRequired,
    children: oneOfType([element, array]),
    className: string,
  };

  static defaultProps = {
    children: [],
    className: '',
  };

  static contextTypes = { [MAP]: object };

  constructor(props, context) {
    super(props);

    this.map = context[MAP];
    this.controlDiv = document.createElement('div');
    this.divIndex = this.map.controls[this.props.position].length;    
    this.map.controls[props.position].push(this.controlDiv);
  }

  componentWillUnmount() {
    this.map.controls[this.props.position].removeAt(this.divIndex);
  }

  render() {
    const { className } = this.props;
    className && this.controlDiv.classList.add(className);

    return createPortal(this.props.children, this.controlDiv);
  }
}

@a8t
Copy link

a8t commented Apr 18, 2019

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. So my code looks roughly like:

const MapControl = (props, context) => {
    const map = context[MAP]
    return null;
    //whatever
}

MapControl.contextTypes = {
  [MAP]: Proptypes.object,
};

Please note that the 16.3 release of React deprecated this API, although it will be included in all 16.x releases.

react-google-maps hasn't been updated in a year. Not sure if it's still being maintained.

@a8t
Copy link

a8t commented Apr 22, 2019

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>

@ila-darji
Copy link

Implemented above example It gives an error.

Cannot read property 'ControlPosition' of undefined

Can I know what is wrong with this?

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

@chunlaw
Copy link

chunlaw commented May 3, 2020

Implemented above example It gives an error.

Cannot read property 'ControlPosition' of undefined

Can I know what is wrong with this?

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

maybe you can try replace google to window.google;
or replace google.maps.ControlPosition.BOTTOM_CENTER to 11 (not recommended)

@jgimbel
Copy link
Author

jgimbel commented May 3, 2020

Wrap the map component with the withScript HOC so the maps library is loaded.

@Fernando-Abreu
Copy link

With this solution, my control was "blinking" every time the map re-rendered, but this didn't happen when I used this solution instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment