Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accessing google.maps.Map Object #161

Closed
bieber opened this issue Nov 30, 2015 · 30 comments
Closed

Accessing google.maps.Map Object #161

bieber opened this issue Nov 30, 2015 · 30 comments

Comments

@bieber
Copy link

bieber commented Nov 30, 2015

I'm running into a little bit of a snag right now, because I'm changing the size of a map's container which means I need to call google.maps.event.trigger(map, 'resize'), where map is the google.maps.Map instance associated with the map, to get Google's JS to recognize the newly resized container. What I'm doing now is using refToMyGoogleMapInstance.props.map, which is a horrible hack that's reaching into your component's internal state (and also I'm not exactly sure how it's working at all, because I'm accessing a prop that I never put there myself. I assume it has something to do with all the component cloning you're doing in your render methods). Would you consider adding (or welcome a PR to add) a getMap() method on GoogleMap to get easier access to the instance you need for triggering events and whatnot?

@tomchentw
Copy link
Owner

I'm actually not a big fan of exposing a map instance to public. However, this is definitely an issue. How about having a triggerEvent utils exposed? Something like:

import {triggerEvent} from "react-google-maps/libs/utils";

function componentDidResized () {
  triggerEvent(this._googleMapComponent, "resize");
}
// and you'll get `this._googleMapComponent` like this:
<GoogleMap ref={(it) => this._googleMapComponent = it} />

// The implementation of `triggerEvent` is quite simple
function triggerEvent (component, ...args) {
  const instance = null /* some magic to get Google Maps instance from react-google-maps component */;
  return google.maps.event.trigger(instance, ...args);
}

@bieber
Copy link
Author

bieber commented Dec 1, 2015

That would also be great. Anything that can save me from having to do
this kludgy hack in my own code is a plus in my book :p

On 11/30/2015 09:21 PM, Tom Chen wrote:

I'm actually not a big fan of exposing a map instance to public.
However, this is definitely an issue. How about having a
|triggerEvent| utils exposed? Something like:

import {triggerEvent}from "react-google-maps/libs/utils";

function componentDidResized () {
triggerEvent(this._googleMapComponent,"resize");
}
// and you'll get this._googleMapComponent like this:
<GoogleMap ref={(it)=> this._googleMapComponent = it}/>

// The implementation of triggerEvent is quite simple
function triggerEvent (component, ...args) {
const instance = null /* some magic to get Google Maps instance from react-google-maps
component */;
return google.maps.event.trigger(instance, ...args);
}


Reply to this email directly or view it on GitHub
#161 (comment).

@tomchentw
Copy link
Owner

Oh cool! Are you writing a book for React? Looking forward to it.

@bieber
Copy link
Author

bieber commented Dec 1, 2015

Hehe, nah, "in my book" is just like "in my opinion"

On 11/30/2015 10:27 PM, Tom Chen wrote:

Oh cool! Are you writing a book for React? Looking forward to it.


Reply to this email directly or view it on GitHub
#161 (comment).

@tomchentw
Copy link
Owner

Hahaha, okay. This happen to me.

@RikkiGibson
Copy link

I'd appreciate this as well. I'm trying to set the center of the map in a "fire-and-forget" way, where certain events might set the map center, but the user is free to move the map around after that. I'm unable to see how to get the "real" current center in handlers for events like onCenterChanged. I can only get the center that I initially set in props.

The only way I can see to set the center through React is through props or state. This setting gets reapplied whenever the component gets updated. So, any time the user clicks on a marker, the map jumps back to the last location the map center was programmatically set.

@tomchentw
Copy link
Owner

Released v4.7.0

@tomchentw
Copy link
Owner

@bieber what the hell is this?

screen shot 2016-01-05 at 5 28 27 pm

@tomchentw
Copy link
Owner

The gmail app is reporting the notification email from GitHub as spam.

@bieber
Copy link
Author

bieber commented Jan 5, 2016

It is, sorry, my email apparently got cracked :/

@tomchentw
Copy link
Owner

Sorry to hear this :( @bieber

@jenyckee
Copy link

The Map object obtained through the ref is not a google.maps.Map Object, it is a react component? Storing this object in a redux store results in a cyclic data structure I think. Is there any way we could obtain the actual google.maps.Map Object?

@tomchentw
Copy link
Owner

@jenyckee yes, it's a React component of course.

There is a hacky way to get underlying google.maps.Map object but it's just an implementation detail. Please check the source code, thanks!

@jenyckee
Copy link

If you use a flux architecture you'll probably want to have access to the map in other components, how can I do this then a not hacky way?

@rewop
Copy link
Collaborator

rewop commented Oct 17, 2016

@jenyckee If you use flux architecture, you can define actions to control the map. The components that wants to update the map can dispatch those actions to fire map updates.

The actions are handled by a store that contains the state of the map.

Finally the component that renders the map would just use the state in your store to implement the logic to render the map. If you need some programmatic updates that use the original google map object, you can place them in this component in the componentWillMount or componentWillUpdate hook or componentWillReceiveProps hook, depending how you bind your component to store updates.

I believe such an approach is usually well decoupled, and not hacky, as your components will not have to rely on implementation details about how to access the original google map instance.

@Thebigbignooby
Copy link

Hi so what is the hacky way to get the google map instance ?, I'v looked through the code but can't figure it out

@michaltakac
Copy link

I think this is not convenient way to work with this library, but...

const service = new google.maps.places.PlacesService(map.context.__SECRET_MAP_DO_NOT_USE_OR_YOU_WILL_BE_FIRED)
service.nearbySearch({
    location: map.getCenter(),
    radius: 500,
    type: ['store']
}, processResults)

function processResults(results, status, pagination) {
    // ...
}

Is there any better way to get PlacesService from Google Places API working with react-google-maps?

@andrewdamelio
Copy link

@michaltakac find a better way?

@michaltakac
Copy link

@andrewdamelio no. And didn't get fired yet.

@sjovall
Copy link

sjovall commented Jun 20, 2017

@tomchentw

Perhaps I've missed something, but I just realized that the getMap() method is not publicly exposed anymore after upgrading to 6.3.

What was the reason behind hiding it?

Like other people have mentioned it's still possible to access it through __SECRET_MAP_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, but is obviously discouraged by the name.

So what's the preferred way of accessing the underlying map for creating custom controls etc?
Or is there a better way of doing this that doesn't involve the map reference?

@sjovall
Copy link

sjovall commented Jun 23, 2017

Ok, so it seems like there is a cleaner way.

A list of constants referring to the underlying deprecated identifiers seems to have been added in version 7.0, so we can import the map constant like this:

import { MAP } from 'react-google-maps/lib/constants

And since its exposed through context we can access it in the child components like this:
static contextTypes = { [MAP]: PropTypes.object };

Still not an ideal solution, but cleaner than using the reference directly through its deprecated name.

@tomchentw
Copy link
Owner

import { MAP } from 'react-google-maps/lib/constants

@sjovall good catch!

@j-quelly
Copy link

import { MAP } from 'react-google-maps/lib/constants'; just gives me a ____SECRET_MAP_DO_NOT_USE_OR_YOU_WILL_BE_FIRED --what am I supposed to do with this?

@tomchentw
Copy link
Owner

@studio174 if you must do such thing, read the source code of v8.0.0. It should be intuitive.

/*
* @url https://developers.google.com/maps/documentation/javascript/3.exp/reference#Map
*/
constructor(props, context) {
super(props, context)
invariant(
!!this.context[MAP],
`Did you wrap <GoogleMap> component with withGoogleMap() HOC?`
)
construct(GoogleMap.propTypes, updaterMap, this.props, this.context[MAP])
}

@devin-yuan
Copy link

@michaltakac find a better way?

@jordanpapaleo
Copy link

@j-quelly that is the constant used to represent the ominous name of the map instance. You can use it to access the map instance and control is as needed. For example you can disable and enable map controsl like this:

import React, {Component} from 'react'
import {withScriptjs, withGoogleMap, GoogleMap} from 'react-google-maps'
import {MAP} from 'react-google-maps/lib/constants'

@withScriptjs
@withGoogleMap
export default class MyGMap extends Component {
  state = {
    disabled: false
  }

  componentDidMount () {
    this.mapInstance = this.mapNode.context[MAP]
  }

  componentDidUpdate (prevProps, prevState) {
    if (prevState.disabled !== this.state.disabled) {
      if (this.state.disabled) {
        this.mapInstance.setOptions({
          disableDoubleClickZoom: false,
          draggable: false,
          scrollwheel: false,
          zoomControl: false
        })
      } else {
        this.mapInstance.setOptions({
          disableDoubleClickZoom: true,
          draggable: true,
          scrollwheel: true,
          zoomControl: true
        })
      }
    }
  }

  render () {
    return (
      <div style={{width: '100%', height: '100%'}}>
        <a onClick={this.setState((currentState) => ({
          disabled: !currentState.disabled
        }))}>toggle disabled</a>

        <GoogleMap ref={(el) => { this.mapNode = el }} />
      </div>
    )
  }
}

@muwednesday
Copy link

@sjovall Could you please give an example? I have started to learn React recnetly, and can not see what you mean.

@muwednesday
Copy link

@jordanpapaleo I tried your code, but got "Cannot read property 'context' of undefined" error.

@muwednesday
Copy link

@sjovall Here is an example.

export class MyPolygon extends React.Component {

    static contextTypes = { [MAP]: PropTypes.object };
    constructor(props, context) {
        super(props, context);
        this.myRef = React.createRef();
    }

    handleRightClick = () => {
        console.log(this.context[MAP]);
    };

    render() {
        return (
            <Polygon ref={this.myRef} path={this.props.path} onRightClick={this.handleRightClick}/>
        )
    }
}

I learned from How to handle React context in a reliable way. Thanks

@ManishSharmaApporio
Copy link

const google = window.google;

it will work please try it .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests