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

Adding ref to access underlying cluster instance. #152

Closed
wants to merge 4 commits into from

Conversation

nikhil2kumar
Copy link

Sometimes we might need to access the cluster engine for performing certain operations or to get all the coordinates within the cluster. This is where this method would come handy.

@venits venits closed this May 5, 2020
@nikhil2kumar
Copy link
Author

nikhil2kumar commented May 6, 2020

@venits Can you please consider looking into this again. You can add the functionality in the next release. You don't necessarily have to merge this approach. Exposing the cluster engine might be helpful in some instances like keeping the track of all the children of the cluster.

@venits
Copy link
Owner

venits commented May 6, 2020

@nikhil2kumar yep I will add this functionality by the end of this week :)

@venits venits reopened this May 6, 2020
@markrickert
Copy link

OMG, thank you for this! I was pulling my hair out for the past few hours, pulling multiple coworkers into why I couldn't get a proper ref for my MapView to change the region in my FunctionComponent.

When i changed back over to the default react-native-maps MapView my code worked so i came here and violla! The answer! I just had to tweak it a bit to work for me.

I applied this PR patch manually and had to also export the map ref in the useImperativeHandle section:

  useImperativeHandle(ref, () => ({
    getClusterEngine: getClusterEngine,
    map: mapRef.current
  }));

I am getting a good map ref back.. For reference, here's how i've implemented it in a FunctionComponent:

import React, { useRef } from "react"
import { observer } from "mobx-react-lite"
import { Dimensions } from "react-native"
import { ParamListBase } from "@react-navigation/native"
import { NativeStackNavigationProp } from "react-native-screens/native-stack"

import MapView from "react-native-map-clustering"

export interface MapScreenProps {
  navigation: NativeStackNavigationProp<ParamListBase>
}

const window = Dimensions.get('window')
const { width, height } = window
const LATITUDE_DELTA = 50
const LONGITUDE_DELTA = LATITUDE_DELTA + (width / height)
const INITIAL_REGION = {
  latitude: 39.828, // Geographic center
  longitude: -98.579, // of the USA
  latitudeDelta: LATITUDE_DELTA,
  longitudeDelta: LONGITUDE_DELTA,
}

export const MapScreen: React.FunctionComponent<MapScreenProps> = observer((props) => {
  const mapRef = useRef(null)

  return (
    <MapView
      ref={mapRef}
      initialRegion={INITIAL_REGION}
      showsUserLocation={true}
      onMapReady={() => {
        console.log('onmapready', mapRef)
      }}
      onUserLocationChange={(e) => {
        const { coordinate } = e.nativeEvent
        if (!coordinate) return
        const { mapView } = mapRef.current

        if (mapView) {
          // Do something here
          // maybe mapView.getCamera()
          // or inspect a property
          // mapView.region
        }
      }}
    />
  )
})

With some other code and markers it allows me to do interactions like this:

() 2020-05-08 14_18_11

@markrickert
Copy link

For reference, here is the patch with my changes:

File: patches/react-native-map-clustering+3.2.0.patch

diff --git a/node_modules/react-native-map-clustering/lib/ClusteredMapView.js b/node_modules/react-native-map-clustering/lib/ClusteredMapView.js
index 6b6e268..da7c9c4 100644
--- a/node_modules/react-native-map-clustering/lib/ClusteredMapView.js
+++ b/node_modules/react-native-map-clustering/lib/ClusteredMapView.js
@@ -1,4 +1,4 @@
-import React, { memo, useState, useEffect, useMemo, useRef } from "react";
+import React, { memo, useState, useEffect, useMemo, useRef, forwardRef, useImperativeHandle } from "react";
 import { Dimensions, LayoutAnimation, Platform } from "react-native";
 import MapView, { Marker, Polyline } from "react-native-maps";
 import SuperCluster from "supercluster";
@@ -11,7 +11,7 @@ import {
   generateSpiral,
 } from "./helpers";
 
-const ClusteredMapView = ({
+const ClusteredMapView = forwardRef(({
   radius,
   maxZoom,
   minZoom,
@@ -33,7 +33,7 @@ const ClusteredMapView = ({
   tracksClusterViewChanges,
   spiralEnabled,
   ...restProps
-}) => {
+}, ref) => {
   const [markers, updateMarkers] = useState([]);
   const [spiderMarkers, updateSpiderMarker] = useState([]);
   const [otherChildren, updateChildren] = useState([]);
@@ -105,7 +105,14 @@ const ClusteredMapView = ({
     }
   }, [isSpiderfier]);
 
-  const _onRegionChangeComplete = (region) => {
+  useImperativeHandle(ref, () => ({
+    getClusterEngine: getClusterEngine,
+    mapView: mapRef.current
+  }));
+
+  const getClusterEngine = () => superCluster;
+
+  const _onRegionChangeComplete = region => {
     if (superCluster) {
       const bBox = calculateBBox(region);
       const zoom = returnMapZoom(region, bBox, minZoom);
@@ -129,8 +136,8 @@ const ClusteredMapView = ({
     }
   };
 
-  const _onClusterPress = (cluster) => () => {
-    const children = superCluster.getLeaves(cluster.id, Infinity);
+  const _onClusterPress = (cluster, limit = Infinity) => () => {
+    const children = superCluster.getLeaves(cluster.id, limit);
     updateClusterChildren(children);
 
     if (preserveClusterPressBehavior) {
@@ -206,7 +213,7 @@ const ClusteredMapView = ({
       })}
     </MapView>
   );
-};
+});
 
 ClusteredMapView.defaultProps = {
   clusteringEnabled: true,

@nikhil2kumar
Copy link
Author

nikhil2kumar commented May 8, 2020

This looks great. :)

@venits
Copy link
Owner

venits commented May 11, 2020

Good news 🎉

In version 3.3.3 I added support for superClusterRef prop that gives you easy access to supercluster instance.

This is example how to use it:

import React, { useRef } from "react";
import MapView from "react-native-map-clustering";

const App = () => {
  const superCluster = useRef();

  return <MapView superClusterRef={superCluster}>
    {/* markers */}
  </MapView>;
};

export default App;

If you want to get all markers inside cluster you can call a function like this:

  const markers = superCluster.current.getLeaves(cluster.id, Infinity);

Thank you for contributing 💖

@venits venits changed the base branch from master to types May 11, 2020 11:41
@venits venits changed the base branch from types to assets May 11, 2020 11:42
@venits venits changed the base branch from assets to master May 11, 2020 11:43
@venits venits closed this May 11, 2020
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

Successfully merging this pull request may close these issues.

None yet

3 participants