I tried wrapping my existing array of Marker objects in a MarkerClusterer and I'm seeing the following odd behavior (the marker clusters flash and disappear occasionally, but sometimes they just show up normally):

Is this a known issue? Any clue what's going on?
I personally don't use MarkerCluster a lot but this seems very weird. One question, do you render your <GoogleMap> in a controlled way or uncontrolled way?
Controlled. (I specify zoom and center props rather than defaults)
Does it matter if the <Marker /> instances are in an array as children of <MarkerClusterer /> instead of "naked" children? (I thought they were essentially the same)
What about if the <Makrer />s have child <InfoWindow /> instances?
let elements = [];
for (let item of iterator) {
if (item === null) continue;
const id = item.id;
const position = item.position;
elements.push(
<Marker
{...props}
key={id}
ref={`marker_${id}`}
position={position}
>
{ info ?
<InfoWindow
{...props}
key={`${info.ref}_info`}
owner={info.ref}
content={info.name} />
: null }
</Marker>
);
}
<MarkerClusterer>{ elements }</MarkerClusterer>
I think it should be children of <MarkerCluster />. I'm suspecting this line of code:
https://github.com/tomchentw/react-google-maps/blob/master/src/addons/addonsCreators/MarkerClustererCreator.js#L96
Also seeing this.
This is also the behavior on their demos it seems: https://googlemaps.github.io/js-marker-clusterer/examples/simple_example.html
And our implementation is using timers it seems: https://github.com/mikesaidani/marker-clusterer-plus/search?utf8=%E2%9C%93&q=setTimeout
@vvo I'm not seeing the problematic behavior on the demo site you linked...
@farrrr Any ideas on this?
@idolize may you give me a reproducible example for me? I think that will help me to trace.
I try to modified example to below. and everything is fine. InfoWindow works fine with MarkerClusterer
import {default as React, Component} from 'react';
import {default as fetch} from 'isomorphic-fetch';
import {GoogleMap, Marker, InfoWindow } from 'react-google-maps';
import {default as MarkerClusterer} from 'react-google-maps/lib/addons/MarkerClusterer';
export default class MarkerClustererExample extends Component {
state = {
markers: [],
}
componentDidMount() {
fetch('https://gist.githubusercontent.com/farrrr/dfda7dd7fccfec5474d3/raw/758852bbc1979f6c4522ab4e92d1c92cba8fb0dc/data.json')
.then(res => res.json())
.then(data => {
this.setState({ markers: data.photos });
});
}
render() {
const { markers } = this.state;
return (
<GoogleMap
containerProps={{
...this.props,
style: {
height: '100%',
},
}}
defaultZoom={ 3 }
defaultCenter={{ lat: 25.0391667, lng: 121.525 }}>
<MarkerClusterer
averageCenter={ true }
enableRetinaIcons={ true }
gridSize={ 60 }>
{ markers.map(marker => (
<Marker
position={{ lat: marker.latitude, lng: marker.longitude }}
key={ marker.photo_id }>
<InfoWindow
key={ `${marker.photo_id}_info` }
content={ marker.photo_title } />
</Marker>
)) }
</MarkerClusterer>
</GoogleMap>
);
}
}
I've been doing some testing by removing chunks from my code, and it seems to be related to using a _controlled_ <GoogleMap> (where I set the new center and zoom props with a handler from onBoundsChanged).
When I remove this code (delete the onBoundsChanged) and just use the uncontrolled <GoogleMap> it works as expected. I will try to make a fiddle or sample repo.
Try this:
import {default as React, Component} from 'react';
import {default as fetch} from 'isomorphic-fetch';
import {GoogleMap, Marker, InfoWindow } from 'react-google-maps';
import {default as MarkerClusterer} from 'react-google-maps/lib/addons/MarkerClusterer';
export default class MarkerClustererExample extends Component {
state = {
markers: [],
zoom: 3,
lat: 25.0391667,
lng: 121.525
}
componentDidMount() {
fetch('https://gist.githubusercontent.com/farrrr/dfda7dd7fccfec5474d3/raw/758852bbc1979f6c4522ab4e92d1c92cba8fb0dc/data.json')
.then(res => res.json())
.then(data => {
this.setState({ markers: data.photos });
});
}
handleBoundsChanged = () => {
if (!this.map) {
// Wait for map to load
return;
}
const center = this.map.getCenter();
const zoom = this.map.getZoom();
const stateCenter = new google.maps.LatLng(this.state.lat, this.state.lng);
// Notice: Check equality here, or it will fire event infinitely
if (zoom !== this.state.zoom || !center.equals(stateCenter)) {
const lat = center.lat();
const lng = center.lng();
this.setState({ lat, lng, zoom });
}
}
render() {
const { markers, zoom, lat, lng } = this.state;
return (
<GoogleMap
ref={ map => this.map = map }
containerProps={{
...this.props,
style: {
height: '100%',
},
}}
onBoundsChanged={ this.handleBoundsChanged }
zoom={ zoom }
center={ ({ lat, lng }) }>
<MarkerClusterer
averageCenter={ true }
enableRetinaIcons={ true }
gridSize={ 60 }>
{ markers.map(marker => (
<Marker
position={{ lat: marker.latitude, lng: marker.longitude }}
key={ marker.photo_id }>
<InfoWindow
key={ `${marker.photo_id}_info` }
content={ marker.photo_title } />
</Marker>
)) }
</MarkerClusterer>
</GoogleMap>
);
}
}
@idolize You need disable InfoWindow autoPan, because InfoWindow will autoPan by default.and it will change center always when it show.
<InfoWindow
options={{ disableAutoPan: true }}
key={ `${marker.photo_id}_info` }
content={ marker.photo_title } />
and everything will be fine.
@farrrr I just tried that and it did not fix the problem. In fact, I think the <InfoWindow>s were a red herring, because I can still reproduce the issue in a controlled <GoogleMap> component without any <InfoWindow>s.
@idolize I will build an online example for u when I back home.
@idolize I made an example here: https://farrrr.github.io/react-google-maps/#basics/marker-clusterer
I suggest change zoom / center to defaultZoom / defaultCenter, because when u move map, it will trigger
handleBoundsChanged serval times, so it made map always repaint. You still can use zoom / center props, but it will be laggy.
@farrrr I just feel some laggy for your example provided. Will adding _.debounce to handleBoundsChanged help?
@tomchentw yep, you are right, I update the example :)
I see the exact same problem. My Map is controlled as well, and after a 2nd render the MarkerClusterer disappears. I noticed that when I then pan the map it will trigger an google maps 'idle' event which in turn triggers a redraw() on the clusterer and it appears again.
Ok, I think I found the problem. Because the zoom property is controlled, the map will generate a zoom_changed when redrawn, but it never generates the idle event. So the viewport is reset, but it is never redrawn.
Ok, I think I found the problem. Because the zoom property is controlled, the map will generate a zoom_changed when redrawn, but it never generates the idle event. So the viewport is reset, but it is never redrawn.
@hmeerlo @lauffenp do you think that this need to be fixed in react-google-maps?
@tomchentw I think it should be fixed, but I have no idea if it is an easy fix. I reverted to the uncontrolled zoom property which was good enough for me.
We're going to look into this shortly.
We're also looking for maintainers. Involve in #266 to help strengthen our community!
I will check it this week...
Has anyone gotten this to work with a controlled map?
Hey, I got this working.
The trouble is indeed with the controlled zoom. react-google-maps sends a map.setZoom(props.zoom) at every render, even when map.getZoom() === props.zoom.
I fixed the situation by doing this:
1) Changed to an uncontrolled zoom.
<GoogleMap ref={this.mapRendered}
zoom={zoom}
center={center} .../
to
<GoogleMap ref={this.mapRendered}
defaultZoom={zoom}
center={center} ... /
2) Store a reference to map in this.mapRendered
3) In componentWillReceiveProps (nextProps) do this:
if (map && map.getZoom() !== nextProps.zoom) {
map.setZoom(nextProps.zoom)
}
And it works.
@mariusandra Awesome! Can you make a PR to handle this directly in the library so developers won't have to worry about doing this themselves if they use a controlled component?
Oh @mariusandra you made my day! Thank you very much. It works like a charm. Willing to see a PR linked to this issue so other devs won't be digging around any longer.
Would love to see an ultimate solution to get rid of this controlled/uncontrolled hell……
@idolize Hi, does your info window also works for the clustered markers, i m trying to do the same but it works only for one marker not for clustered markers. Please help me if you have any idea how can i show info windows for the clustered markers as well.
Just to let you know 6.0.0 is released on npm beta tag now. We also have a new demo page. Feel free to try it:
https://tomchentw.github.io/react-google-maps/
I had this issue when using fitBounds using refs on the <GoogleMap /> component. It's fine if I wrap fitBounds in Ramda's once so that we only fit to bounds once — which does it make sense. After that it's perfectly fine.
const fitBounds = once((map, bounds) => map.fitBounds(bounds));
// ...
<GoogleMap ref={map => fitBounds(map, bounds)} />
Most helpful comment
Hey, I got this working.
The trouble is indeed with the controlled zoom.
react-google-mapssends amap.setZoom(props.zoom)at every render, even whenmap.getZoom() === props.zoom.I fixed the situation by doing this:
1) Changed to an uncontrolled zoom.
to
2) Store a reference to
mapinthis.mapRendered3) In
componentWillReceiveProps (nextProps)do this:And it works.