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

Unmounting Markers performance using MarkerClusterer #2849

Open
csarellano opened this issue Sep 5, 2021 · 10 comments
Open

Unmounting Markers performance using MarkerClusterer #2849

csarellano opened this issue Sep 5, 2021 · 10 comments

Comments

@csarellano
Copy link

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
Screen Shot 2021-09-05 at 11 11 19 AM

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?

@Hector1567XD
Copy link

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
image

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:
image

And it takes several seconds to finish removing the old markers to re-render the new ones.

Solution

image

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 .

@Hector1567XD
Copy link

Hector1567XD commented Sep 6, 2021

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:
tomchentw/react-google-maps#836 (comment)
But I have not quite understood the solution he proposes.

@ivannovazzi
Copy link

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?

@madox2
Copy link

madox2 commented Aug 13, 2022

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>
  )
}

@hamez0r
Copy link

hamez0r commented Sep 2, 2022

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.

@ivannovazzi
Copy link

@madox2 Thanks for your suggestion, it helped a lot in my specific case and I haven't observed anything else than improved performance.

@alifdarsim
Copy link

alifdarsim commented Oct 8, 2022

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.

@JustFly1984
Copy link
Owner

please test MarkerClustererF with 2.15.0 version

@dylantf
Copy link

dylantf commented Dec 6, 2022

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.

@releshreyas
Copy link

releshreyas commented Jul 21, 2023

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>)

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

9 participants