react-google-maps-api
react-google-maps-api copied to clipboard
Unmounting Markers performance using MarkerClusterer
Hello everyone!
I'm currently rendering and clustering more than a 1500 markers. On the first load everything works ok but if I change the state filtering those markers it takes too long (1min) to remove all the markers and just leave the ones I want. It only happens if I use the MarkerClusterer component.
This is what I see while it is removing the markers
I found that the same issue happened on the react-google-maps library
I'm using: os: mac/linux node --version: 16.3.0 react version: 17.0.2 @react-google-maps/api: 2.2.0
Any workaround to resolve this issue?
Hi mate :), we seem to be studying this problem at the same time. I share with you what I have found
Context
I built this prototype
function EventsMap(props) {
console.log('Rendered: Cluster')
return (
<MarkerClusterer options={options}>
{(clusterer) => {
return props.markers.map(marker =>
<Marker position={marker.position} key={marker.id} clusterer={clusterer} />
)
}
}
</MarkerClusterer>
)
}
Problem
When you change "tabs" or filters, this changes the marker array causing react to have to remove the previous markers and causing the markers to be generated.
Which causes it to have a behavior similar to this:
And it takes several seconds to finish removing the old markers to re-render the new ones.
Solution
I'm not an expert in react or google maps so I'm not sure of the theoretical basis of this, but it seems that if you "help" React to remove the old markers before rendering the new ones with clusterer.clearMarkers();
the performance goes up a lot.
New Problem (For me)
The problem I have now is that when a new marker arrives in a new tab, the previous ones are erased and only this one remains
If you have a static map that will not receive new markers with Push events or websockets this will be a problem for you, but for me it is.
I think what I will do is to execute clusterer.clearMarkers(); when the tab/filter change is done 🤔... in any case I have to look for how to access clusterer from outside <ClustersMarker>.
Something else that improves performance is to add noClustererRedraw={true}
to each marker, in fact I think it is the right way to deal with this problem, the documentation talks a bit about it in:
https://react-google-maps-api-docs.netlify.app/#!/Marker
But although it solves my performance problems it adds me another problem, the marker does not reload itself, then I have to "move" or interact with it so that new markers are added. 🤔
UPDATE 1:
In another thread someone seems to have solved this: https://github.com/tomchentw/react-google-maps/issues/836#issuecomment-894381349 But I have not quite understood the solution he proposes.
Hello everyone! I am dealing with this issue and I can't find a way to actually make it work correctly, meaning markers and clusters do get updated correctly. Did anyone found a comprehensive working solution so far?
Setting noClustererRedraw
and re-painting clusterer everytime markers change boosted performance significantly. E.g. in my case I re-paint whenever number of markers has changed:
function Map({ markers }) {
const clustererRef = useRef()
useEffect(() => {
clustererRef.current?.repaint()
}, [markers.length])
return (
<GoogleMap center={center}>
<MarkerClusterer
options={clustererOptions}
onLoad={clusterer => (clustererRef.current = clusterer)}
>
{clusterer =>
markers.map(marker => (
<MarkerF
key={marker.id}
position={marker.position}
clusterer={clusterer}
noClustererRedraw={true}
/>
))
}
</MarkerClusterer>
</GoogleMap>
)
}
I confirm calling repaint
is helping a lot.
We had another problem: we have marker arrays that differ in size (from a couple of hundreds to tens of thousands) and when the size was greater than ten thousands, nothing was painted.
We ended up playing with clusterer.batchSize
, and a good value seems to be markers.length / 20
.
@madox2 Thanks for your suggestion, it helped a lot in my specific case and I haven't observed anything else than improved performance.
Solution by @madox2 works fantastically. I can confirm repaint
the clusterer every time data changes help in performance with big data and some other weird issues.
please test MarkerClustererF with 2.15.0 version
Hi @JustFly1984 I just upgraded from 2.8 to 2.17 and the performance seems the same without manually calling repaint
as described above. I ran a performance profile in Chrome, with the default clusterer behavior, the "scripting" takes around 5 seconds on my macbook with ~800 markers (same on both 2.8 and 2.17). Using noClustererRedraw
+ manually calling repaint
brings this down to 1.5 seconds.
If anyone wants a solution for React18, I resolved this by using a combination of state management and clearing of markers, you also need to use MarkerClustererF and MarkerF as they are compatible with React18.
When updating your set of markers, set the state to an empty array and then repopulate it with the desired markers and then use the onUnMount handle to clear the set of markers.
What happens is that when the markers length is set to 0, the onUnMount handler is called which clears the markers and then when you repopulate the markers the markerclusterer is rendered from scratch which means no repaint needed.
Hope this helps!
markers && markers.length > 0 && (<MarkerClustererF ... onUnmount={(clusterer) => clusterer.clearMarkers()} > {(clusterer: Clusterer) => {markers.map(marker => <MarkerF.../>)} </MarkerClustererF>)