Hackernoon logoOptimize React Native Map in Your Applications [A How To Guide] by@amadeuspham

Optimize React Native Map in Your Applications [A How To Guide]

Author profile picture

@amadeusphamHarry Pham

I love building cross-platform mobile apps using React Native to get rid of the boring tasks.

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.

What the problem is

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.
Photo credit: EduBirdie
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.

How to solve it

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.

Conclusion

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:
  1. Turn off
    trackViewChanges
    in custom Markers when not needed.
  2. Use
    React.memo
    , or
    shouldComponentUpdate
    , or
    React.PureComponent
    .
  3. Use “icon” instead of “image” to build a custom Marker.
  4. Cluster your Markers.
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 emailvisit my Github and connect with me on LinkedIn.

Tags

The Noonification banner

Subscribe to get your daily round-up of top tech stories!