Skip to content

Commit 2558be6

Browse files
authored
Finalize MapView performance optimizations (#802)
* Revert "Add prop to improve performance of MapView (#800)" This reverts commit 69d8e28. * Memoize cluster components * Revert "Memoize cluster components" This reverts commit 653cd0f. * Memoize default cluster view component * Revert "Memoize default cluster view component" This reverts commit 975b7ea. * Optimize auto clustering * More optimizations to auto clustering * Allow a way to use default `icon` prop * Fix condition * Disable tracksViewChanges on default cluster * Fix typo
1 parent 6767f10 commit 2558be6

File tree

5 files changed

+70
-73
lines changed

5 files changed

+70
-73
lines changed

packages/maps/src/__tests__/MapView.test.tsx

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,6 @@ describe("MapView tests", () => {
186186
}
187187
}
188188
onPress={[Function]}
189-
tracksViewChanges={true}
190189
/>,
191190
<Marker
192191
coordinate={
@@ -196,7 +195,6 @@ describe("MapView tests", () => {
196195
}
197196
}
198197
onPress={[Function]}
199-
tracksViewChanges={true}
200198
/>,
201199
],
202200
[
@@ -208,7 +206,6 @@ describe("MapView tests", () => {
208206
}
209207
}
210208
onPress={[Function]}
211-
tracksViewChanges={true}
212209
/>,
213210
<Marker
214211
coordinate={
@@ -218,7 +215,6 @@ describe("MapView tests", () => {
218215
}
219216
}
220217
onPress={[Function]}
221-
tracksViewChanges={true}
222218
/>,
223219
],
224220
]
@@ -251,7 +247,6 @@ describe("MapView tests", () => {
251247
}
252248
}
253249
onPress={[Function]}
254-
tracksViewChanges={true}
255250
/>,
256251
<Marker
257252
coordinate={
@@ -261,7 +256,6 @@ describe("MapView tests", () => {
261256
}
262257
}
263258
onPress={[Function]}
264-
tracksViewChanges={true}
265259
/>,
266260
],
267261
[
@@ -273,7 +267,6 @@ describe("MapView tests", () => {
273267
}
274268
}
275269
onPress={[Function]}
276-
tracksViewChanges={true}
277270
/>,
278271
<Marker
279272
coordinate={
@@ -283,7 +276,6 @@ describe("MapView tests", () => {
283276
}
284277
}
285278
onPress={[Function]}
286-
tracksViewChanges={true}
287279
/>,
288280
],
289281
]
@@ -340,7 +332,6 @@ describe("MapView tests", () => {
340332
}
341333
}
342334
onPress={[Function]}
343-
tracksViewChanges={true}
344335
/>,
345336
<Marker
346337
coordinate={
@@ -350,7 +341,6 @@ describe("MapView tests", () => {
350341
}
351342
}
352343
onPress={[Function]}
353-
tracksViewChanges={true}
354344
/>,
355345
],
356346
]

packages/maps/src/components/MapMarker.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
View,
66
StyleSheet,
77
Text,
8+
Platform,
89
} from "react-native";
910
import { Marker as MapMarkerComponent } from "./react-native-maps";
1011
import type {
@@ -20,6 +21,7 @@ export interface MapMarkerProps
2021
longitude: number;
2122
pinImage?: string | ImageSourcePropType;
2223
pinImageSize?: number;
24+
androidUseDefaultIconImplementation?: boolean;
2325
onPress?: (latitude: number, longitude: number) => void;
2426
}
2527

@@ -40,6 +42,7 @@ export function renderMarker(
4042
longitude,
4143
pinImage,
4244
pinImageSize = 50,
45+
androidUseDefaultIconImplementation = false,
4346
onPress,
4447
children,
4548
title,
@@ -76,6 +79,9 @@ export function renderMarker(
7679
);
7780
}
7881

82+
const shouldUseDefaultIconImplementation =
83+
Platform.OS === "android" && androidUseDefaultIconImplementation;
84+
7985
return (
8086
<MapMarkerComponent
8187
ref={ref}
@@ -89,11 +95,18 @@ export function renderMarker(
8995
const coordinate = event.nativeEvent.coordinate;
9096
onPress?.(coordinate.latitude, coordinate.longitude);
9197
}}
98+
icon={
99+
shouldUseDefaultIconImplementation
100+
? typeof pinImage === "string"
101+
? { uri: pinImage }
102+
: (pinImage as any)
103+
: undefined
104+
}
92105
{...rest}
93106
>
94107
{nonCalloutChildren}
95108

96-
{pinImage && (
109+
{pinImage && !shouldUseDefaultIconImplementation && (
97110
<Image
98111
testID="map-marker-pin-image"
99112
source={typeof pinImage === "string" ? { uri: pinImage } : pinImage}

packages/maps/src/components/MapView.tsx

Lines changed: 46 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,6 @@ export interface MapViewProps<T>
4242
longitude?: number;
4343
autoClusterMarkers?: boolean;
4444
autoClusterMarkersDistanceMeters?: number;
45-
// Improves performance when panning by temporarily preventing markers from tracking view changes
46-
// See `tracksViewChanges`: https://github.com/react-native-maps/react-native-maps/blob/master/docs/marker.md#props
47-
disableTrackViewChangesWhenPanning?: boolean;
4845
markersData?: T[];
4946
keyExtractor?: (item: T, index: number) => string;
5047
renderItem?: ({ item, index }: { item: T; index: number }) => JSX.Element;
@@ -62,7 +59,6 @@ const MapViewF = <T extends object>({
6259
loadingEnabled = true,
6360
autoClusterMarkers = false,
6461
autoClusterMarkersDistanceMeters = 1000,
65-
disableTrackViewChangesWhenPanning = true,
6662
markersData,
6763
keyExtractor,
6864
renderItem,
@@ -77,8 +73,6 @@ const MapViewF = <T extends object>({
7773
animateToLocation: (location: ZoomLocation) => void;
7874
mapRef: React.RefObject<MapViewComponent>;
7975
}) => {
80-
const [markerTracksViewChanges, setMarkerTracksViewChanges] =
81-
React.useState(true);
8276
const [currentRegion, setCurrentRegion] = React.useState<Region | null>(null);
8377
const delayedRegionValue = useDebounce(currentRegion, 300);
8478

@@ -190,14 +184,20 @@ const MapViewF = <T extends object>({
190184
const clusterMarkers = React.useCallback(
191185
(
192186
markers: React.ReactElement[],
193-
clusters: React.ReactElement[],
194187
distanceMeters: number,
195188
clusterView?: React.ReactElement
196189
) => {
190+
const clusters = [];
191+
const clusteredMarkers: React.ReactElement[] = [];
192+
197193
for (const marker of markers) {
198194
const { latitude: lat, longitude: long } =
199195
marker.props as MapMarkerProps;
200196

197+
if (clusteredMarkers.includes(marker)) {
198+
continue;
199+
}
200+
201201
const nearbyMarkers = getNearbyMarkers(
202202
lat,
203203
long,
@@ -207,7 +207,7 @@ const MapViewF = <T extends object>({
207207

208208
if (nearbyMarkers.length > 1) {
209209
for (const nearbyMarker of nearbyMarkers) {
210-
markers.splice(markers.indexOf(nearbyMarker), 1);
210+
clusteredMarkers.push(nearbyMarker);
211211
}
212212
clusters.push(
213213
<MapMarkerCluster>
@@ -217,6 +217,12 @@ const MapViewF = <T extends object>({
217217
);
218218
}
219219
}
220+
221+
const unClusteredMarkers = markers.filter(
222+
(marker) => !clusteredMarkers.includes(marker)
223+
);
224+
225+
return { clusters, unClusteredMarkers };
220226
},
221227
[getNearbyMarkers]
222228
);
@@ -241,36 +247,50 @@ const MapViewF = <T extends object>({
241247
};
242248

243249
callOnRegionChange();
244-
245250
// onRegionChange excluded to prevent calling on every rerender when using an anonymous function (which is most common)
246251
// eslint-disable-next-line react-hooks/exhaustive-deps
247252
}, [delayedRegionValue]);
248253

249-
const markers = React.useMemo(
250-
() => getChildrenForType(MapMarker),
251-
[getChildrenForType]
252-
);
253254
const circles = React.useMemo(
254255
() => getChildrenForType(MapCircle),
255256
[getChildrenForType]
256257
);
257-
const clusters = React.useMemo(
258+
259+
const markers = React.useMemo(
260+
() => getChildrenForType(MapMarker),
261+
[getChildrenForType]
262+
);
263+
264+
const manualClusters = React.useMemo(
258265
() => getChildrenForType(MapMarkerCluster),
259266
[getChildrenForType]
260267
);
268+
261269
const clusterView = React.useMemo(() => {
262270
const clusterViews = getChildrenForType(MapMarkerClusterView);
263271
return clusterViews.length ? clusterViews[0] : undefined; //Only take the first, ignore any others
264272
}, [getChildrenForType]);
265273

266-
if (autoClusterMarkers) {
267-
clusterMarkers(
268-
markers,
269-
clusters,
270-
autoClusterMarkersDistanceMeters,
271-
clusterView
272-
);
273-
}
274+
const { clusters, unClusteredMarkers } = React.useMemo(() => {
275+
if (autoClusterMarkers) {
276+
const { clusters, unClusteredMarkers } = clusterMarkers(
277+
markers,
278+
autoClusterMarkersDistanceMeters,
279+
clusterView
280+
);
281+
282+
return { clusters: clusters.concat(manualClusters), unClusteredMarkers };
283+
} else {
284+
return { clusters: manualClusters, unClusteredMarkers: markers };
285+
}
286+
}, [
287+
autoClusterMarkers,
288+
autoClusterMarkersDistanceMeters,
289+
markers,
290+
manualClusters,
291+
clusterView,
292+
clusterMarkers,
293+
]);
274294

275295
const memoizedMapView = useDeepCompareMemo(
276296
() => (
@@ -290,31 +310,16 @@ const MapViewF = <T extends object>({
290310
initialCamera={camera}
291311
loadingEnabled={loadingEnabled}
292312
onRegionChange={setCurrentRegion}
293-
onTouchStart={() => {
294-
if (disableTrackViewChangesWhenPanning) {
295-
setMarkerTracksViewChanges(false);
296-
}
297-
}}
298-
onTouchEnd={() => {
299-
if (disableTrackViewChangesWhenPanning) {
300-
setMarkerTracksViewChanges(true);
301-
}
302-
}}
303313
onPress={(event) => {
304314
const coordinate = event.nativeEvent.coordinate;
305315
onPress?.(coordinate.latitude, coordinate.longitude);
306316
}}
307317
style={[styles.map, style]}
308318
{...rest}
309319
>
310-
{markers.map((marker, index) =>
320+
{unClusteredMarkers.map((marker, index) =>
311321
renderMarker(
312-
{
313-
...marker.props,
314-
tracksViewChanges: disableTrackViewChangesWhenPanning
315-
? markerTracksViewChanges
316-
: undefined,
317-
},
322+
marker.props,
318323
index,
319324
getMarkerRef(getMarkerIdentifier(marker.props)),
320325
() => dismissAllOtherCallouts(getMarkerIdentifier(marker.props))
@@ -333,13 +338,7 @@ const MapViewF = <T extends object>({
333338
}}
334339
>
335340
{clusters.map((cluster, index) => (
336-
<React.Fragment key={index}>
337-
{React.cloneElement(cluster, {
338-
tracksViewChanges: disableTrackViewChangesWhenPanning
339-
? markerTracksViewChanges
340-
: undefined,
341-
})}
342-
</React.Fragment>
341+
<React.Fragment key={index}>{cluster}</React.Fragment>
343342
))}
344343
</MapMarkerContext.Provider>
345344

@@ -357,7 +356,7 @@ const MapViewF = <T extends object>({
357356
loadingEnabled,
358357
longitude,
359358
mapRef,
360-
markers,
359+
unClusteredMarkers,
361360
onPress,
362361
onRegionChange,
363362
provider,
@@ -366,8 +365,6 @@ const MapViewF = <T extends object>({
366365
showsCompass,
367366
style,
368367
zoom,
369-
markerTracksViewChanges,
370-
disableTrackViewChangesWhenPanning,
371368
]
372369
);
373370

packages/maps/src/components/marker-cluster/MapMarkerCluster.tsx

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,15 @@ import { MapViewContext } from "../MapViewCommon";
99
import { flattenReactFragments } from "@draftbit/ui";
1010
import { MapMarkerContext } from "../MapView";
1111

12-
interface MapMarkerClusterProps {
13-
tracksViewChanges?: boolean;
14-
}
15-
1612
/**
1713
* Component that clusters all markers provided in as children to a single point when zoomed out, and shows the markers themselves when zoomed in
1814
* Renders a default component that shows the number of components inside cluster
1915
*
2016
* Also accepts MapMarkerClusterView to override the rendered cluster component
2117
*/
22-
const MapMarkerCluster: React.FC<
23-
React.PropsWithChildren<MapMarkerClusterProps>
24-
> = ({ children: childrenProp, tracksViewChanges }) => {
18+
const MapMarkerCluster: React.FC<React.PropsWithChildren> = ({
19+
children: childrenProp,
20+
}) => {
2521
const { region, animateToLocation } = React.useContext(MapViewContext);
2622

2723
const children = React.useMemo(
@@ -79,18 +75,18 @@ const MapMarkerCluster: React.FC<
7975
longitude,
8076
children: clusterView,
8177
onPress,
82-
tracksViewChanges,
78+
tracksViewChanges:
79+
clusterView.type === DefaultMapMarkerClusterView
80+
? false
81+
: clusterView.props.tracksViewChanges,
8382
})}
8483
</MapMarkerClusterContext.Provider>
8584
);
8685
}}
8786
>
8887
{markers.map((marker, index) =>
89-
renderMarker(
90-
{ ...marker.props, tracksViewChanges },
91-
index,
92-
getMarkerRef(marker.props),
93-
() => onMarkerPress(marker.props)
88+
renderMarker(marker.props, index, getMarkerRef(marker.props), () =>
89+
onMarkerPress(marker.props)
9490
)
9591
)}
9692
</MarkerClusterer>

packages/maps/src/components/marker-cluster/MapMarkerClusterView.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ interface MapMarkerClusterViewProps {
77
zoomOnPress?: boolean;
88
onPress?: (latitude: number, longitude: number) => void;
99
renderItem?: ({ markerCount }: { markerCount: number }) => JSX.Element;
10+
tracksViewChanges?: boolean;
1011
style?: StyleProp<ViewStyle>;
1112
}
1213

0 commit comments

Comments
 (0)