Julian Rex
Julian Rex

Reputation: 89

Creating Custom Buttons in React Google Maps

I am currently working on a personal project using react-google-maps and I am having trouble displaying custom buttons on the map. I want to create a menu button that hovers over the map, just like the top left corner of Google Maps (search bar).

From what the documentation says, you have to create a div and use the following command to put it on a specific side of the map.

map.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(<div>);

However, I cannot get this to work nicely with React, as the Google docs are in vanilla js. Where is a good place to put this line of code in React? I know that in vanilla js you can pretty much put it anywhere.

I also looked into the react-google-maps docs but it doesn't seem like this particular use case is implemented in the API.

Is there a way to easily create a custom button that hovers over the map in React or with react-google-maps? If not is there a neat hack to get this to work somehow? Or maybe a CSS trick I'm not aware of?

Here is some sample code that I currently have for the project:

Code sandbox

MapContainer.js

import React from 'react';
import { GoogleMap, withScriptjs, withGoogleMap } from "react-google-maps";
import { compose, withProps } from "recompose";
import Menu from "./Menu";

// -----------------------------------------------------------------------------------------
// needed to construct Google Maps in React

const styles = require('../../assets/styles/GoogleMapStyles.json');


const MyMapComponent = compose(
    withProps({
        googleMapURL: "https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=YOUR_API_KEY",
        loadingElement: < div style={{ height: `100%` }} />,
        containerElement: < div style={{ height: `100%` }} />,
        mapElement: < div style={{ height: `100%` }} />
    }),
    withScriptjs,
    withGoogleMap
)((props) =>

    <GoogleMap
        defaultZoom={15}
        defaultCenter={props.location}
        defaultOptions={{styles: styles,
            disableDefaultUI: true}}
    >
        <Menu /> // the hamburger icon that I want hovered over the map
    </GoogleMap>
);

// -----------------------------------------------------------------------------------------

export default class MapContainer extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            user: {
                currentLocation: {lat: null, lng: null}
            },
            map: null
        };

    }

    componentDidMount() {
        .....
    }

    render() {
        return(
            <div id="map-container" ref="map">
                <MyMapComponent 
                location={this.state.user.currentLocation}/>
            </div>
        );
    }
}

Menu.js


import React from 'react';
import '@fortawesome/fontawesome-free/css/all.min.css';

export default class Menu extends React.Component {
    constructor(props) {
        super(props);
    }

    render() { 
        return(
            <div>
                <span className="menu-btn">
                    <i class="fas fa-bars"></i>
                </span>
            </div>
        );
    }
}

Thanks in advance for your help. Any idea/comment/insight is appreciated at this point. I've been thinking about this problem for quite some time and I'm now in a place where I cannot ignore it any longer...

Upvotes: 4

Views: 6733

Answers (1)

Sam Keays
Sam Keays

Reputation: 746

There is an easy way of doing this if you are using >= React 16.0:

Using a pregenerated div and ReactDom.createPortal inside the return value of the Map component and calling onload with the map parameter to add the div like so:

import React from 'react';
import ReactDom from 'react-dom';
import { GoogleMap, withScriptjs, withGoogleMap } from "react-google-maps";
import { compose, withProps } from "recompose";
import Menu from "./Menu";

// -----------------------------------------------------------------------------------------
// needed to construct Google Maps in React

const styles = require('../../assets/styles/GoogleMapStyles.json');
const controlButtonDiv = document.createElement('div');
const handleOnLoad = map => {
  map.controls[google.maps.ControlPosition.TOP_RIGHT].push(controlButtonDiv);
};

const MyMapComponent = compose(
    withProps({
        googleMapURL: "https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=YOUR_API_KEY",
        loadingElement: < div style={{ height: `100%` }} />,
        containerElement: < div style={{ height: `100%` }} />,
        mapElement: < div style={{ height: `100%` }} />
    }),
    withScriptjs,
    withGoogleMap
)((props) =>

    <><GoogleMap
        defaultZoom={15}
        defaultCenter={props.location}
        defaultOptions={{styles: styles,
            disableDefaultUI: true}}
        onLoad={map => handleOnLoad(map)
    >
    </GoogleMap>,
    ReactDom.createPortal(<Menu />, controlButtonDiv),
    </>
);

I actually did this with google-map-react library but it is the same idea, you just need to add

yesIWantToUseGoogleMapApiInternals
onGoogleApiLoaded={({ map, maps }) => handleOnLoad(map, maps)}

as attributes and do the same thing.

Upvotes: 4

Related Questions