paint-brush
Optimize React Native Map in Your Applications [A How To Guide]by@amadeuspham
16,800 reads
16,800 reads

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

by Harry PhamJanuary 13th, 2020
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Four ways to make your React Native map faster, smoother, and more responsive. The performance of React Native maps will get worse the more data it has to handle. Turn off trackViewChanges in custom Markers when not needed. Only allow Markers to be re-rendered when the state of the Marker itself changes. Use React React to create a map using React Native's React-Native map API. The default Apple map in React Native runs very slowly on iOS Simulator, while Google's Google map does not.
featured image - Optimize React Native Map in Your Applications [A How To Guide]
Harry Pham HackerNoon profile picture

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.

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.