Four ways to make your React Native map become a lot faster and more responsive.
Why this is important
As a mobile developer working with React Native or any platform/language, you will sooner or later have to deal with maps. And when that time comes, you need to know how to optimize it effectively, because if not, your users’ experience will greatly suffer. In this article I will show you — in 4 possible ways — how to make your map faster, smoother, and more responsive.
Putting a map and use it in your React Native application is dead simple because of react-native-maps. It’s a straightforward API, you only need to render a MapView to display a blank map, then render a bunch of Markers, which are essentially pins on a map.
If you are not familiar with react-native-maps, make sure to check out their documentation before continue.
The problem is, as with a lot of other components, the performance of a React Native map will get worse the more data it has to handle. Displaying 5 Markers for your favorite cafes around the city would be a walk in the park; but let say, if your app shows all McDonald’s in the country, there will be hundreds or thousands of Markers if you zoom out large enough! You will certainly experience lags up to a point where your app is effectively unusable.
Delays in React Native are commonly caused by excessive re-rendering, simply means that your app renders components more frequently than it should, wasting a lot of time. They are usually caused by changes in part of the state unrelated to the component (Markers in this case). If we only allow re-renders when necessary — when the state of the Marker itself changes — the performance of the Map would be significantly better.
So we identified the problem and came up with a solution. But before we go further, let me first give you a small relief: your map might not be as slow as it appears.
If you happen to be viewing your app in the iOS Simulator on macOS, and you are using Google as your map provider like this:
<MapView style={{flex: 1}} provider='google'>
I might have good news for you. For some reasons, the Google-provided map in React Native runs very slowly on iOS Simulator, while the default Apple map does not (just remove
provider='google'
and you will see). To better access its performance on iOS, make sure to have an iPhone which you can test the app on.1. Turn off trackViewChanges in custom Markers when not needed
The
Marker
component comes with a default trackViewChanges
parameter set to true. This parameter enables custom Markers to track changes in the view and be redrawn. If your application does not require this feature, simply set trackViewChanges
to false will significantly increase speed.import {Marker} from 'react-native-maps';
export default function MyMarker() {
return(
<Marker
key={pin.id}
coordinate={pin.coord}
title={pin.name}
onPress={onPinChosen}
image={../assets/pin.png}
tracksViewChanges={false}
/>
);
}
2. React.memo, or shouldComponentUpdate, or PureComponent
Another way to minimize rendering is to only allow your
Marker
to be re-rendered when its props actually change (and usually they don’t: when is the last time your house move when you’re looking at it on the map?). React provide us with React.memo (for function components) and shouldComponentUpdate (for class components). Alternatively, you can also use React.PureComponent to achieve the same result as shouldComponentUpdate
Firstly, an example of
React.memo
:import React from 'react';
import { Marker } from 'react-native-maps';
function CarPin(props) {
const {coord} = props;
return (
<Marker
coordinate={coord}
tracksViewChanges={false}
/>
);
}
export default CarMarker = React.memo(CarPin);
an example of
shouldComponentUpdate
:import React from 'react';
import { Marker } from 'react-native-maps';
export default class CarPin extend React.Component {
shouldComponentUpdate(nextProps) {
return nextProps.coord !== this.props.coord
}
render(){
return (
<Marker
coordinate={this.props.coord}
tracksViewChanges={false}
/>
);
}
}
and an example of
React.PureComponent
:import React from 'react';
import { Marker } from 'react-native-maps';
export default class CarPin extend React.PureComponent {
render(){
return (
<Marker
coordinate={this.props.coord}
tracksViewChanges={false}
/>
);
}
}
Make sure to check out React.memo , shouldComponentUpdate and React.PureComponent official documentation if you want to further customize the behavior of your component.
3. Use “icon” instead of “image” to build your custom Marker
The performance difference is not explicitly said in the documentation, but according to the official source code:
/**
* A custom image to be used as the marker's icon. Only local image resources are allowed to be used.
*/
image: PropTypes.any,
/**
* Marker icon to render (equivalent to `icon` property of GMSMarker Class).Using this property has an advantage over `image` in term of performance, battery usage... because it doesn't trigger tracksViewChanges. (tracksViewChanges is set to YES by default if iconView is not nil).
*/
icon: PropTypes.any,
4. Cluster your Markers
Instead of explaining what clustering means in this case, let me show you an example from an app I built. The images below shows a map with clustering (left) and a map without clustering (right)
Map clustering can be done easily using APIs such as react-native-map-clustering or @bam.tech/react-native-component-map-clustering
While the difference in appearance is already impressive, the difference in performance is even more amazing. To understand why, we have to look at how map clustering works:
First, the API will count the number of Markers located in a (relatively) small region on the
MapView
. Instead of rendering the Markers, it will render the number of Markers on the map. When the user zoom in or zoom out, the number will be calculated again. When the user zoom in enough, the actual Markers will be displayed.This means, the app will save a lot of time from rendering a whole bunch of Markers. Instead it will only count and render the number of Markers. This is also where you should be careful — since the app will have to count the number of Markers every time the user zooming in or out, make sure that the trade off between rendering time and recounting time is worth it.
Optimizing React Native map, while relatively simple, is not something straightforward that is automatically done for you by the APIs. In this article I have listed 4 different ways to do that:
trackViewChanges
in custom Markers when not needed.React.memo
, or shouldComponentUpdate
, or React.PureComponent
.I hope by using a these tips, you will be able to build faster, more responsive applications that can provide a superb user experience. Best of luck!
If you are interested in what I do, or want to contact me, feel free to send me an email, visit my Github and connect with me on LinkedIn.