Constructing a lot of markers on react-leaflet map is too slow
Asked Answered
O

3

17

I have a dataset of 4360 geomarkers that I want to display on the Leaflet map. CircleMarker works just fine and the performance of the constructed map is ok. However, constructing the map takes too much time (around 20 seconds). Without react it takes a fraction of second to construct the markers. Is there a some performance hint or trick that can be used to make it construct the map faster?

import * as React from 'react';
import { Component } from 'react';
import { LatLng } from 'leaflet';
import { Map, TileLayer, CircleMarker, Popup } from 'react-leaflet';

export default class Editor extends Component {
    state = {
        lat: 51.505,
        lng: -0.09,
        zoom: 13,
        markers : [ ]
    }

    componentDidMount() {
        // data.csv contains several thousands of items
        fetch('data.csv')
            .then(res => res.json())
            .then(data => this.setState({ markers: data.items.map(v => new LatLng(v.lat, v.lng)) }));
    }

    render() {
        const markers = this.state.markers.map((v, i) =>
            <CircleMarker key={i} center={v} radius={3} />);
        return (
            <Map center={new LatLng(51.505, -0.09)} zoom={this.state.zoom}>
                <TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
                {markers}
            </Map>
        )
    }
}

Direct DOM manipulation does it in a fraction of a second:

export default class ItemsMap extends React.Component {
  state = { items : [ ] };
  map : L.Map;

  componentDidUpdate(prevProps : any, prevState : any) {
    this.renderItems(this.state.items);
  }

  componentDidMount() {
    const node : any = ReactDOM.findDOMNode(this);
    this.map = L.map(node).setView([51.505, -0.09], 13);
    L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 18 }).addTo(this.map);

    fetch('data.csv')
      .then(res => res.json())
      .then(data => this.setState({ items: data.items.map(v => new LatLng(v.lat, v.lng)) }));
  }

  renderItems(items : Array<any>) {        
    items.forEach(item => {
      L.circleMarker(
        [ item.lat, item.lng ],
        { radius : 3 }
      ).addTo(this.map);
    });
  }

  render() {
    return (
      <div id="mapid" style={{ height: '100%' }} />
    );
  }
}
Ochre answered 3/10, 2018 at 5:36 Comment(4)
Can you post the non-react code that is running significantly faster?Nierman
@jcdizzle94 - added to the questionOchre
Did you ever figure out how to optimize the rendering of these markers? I'm running into a similar problem with the GeoJSON component and not even that many geometries. 100 easily slow down my application (during rendering).Extremity
faced same issue. Direct DOM manipulation works fastSwisher
C
11

One technique to consider would be to render only a subset of markers within a given map boundary, it can dramatically reduce the time it takes to re-render the components as well as the number of DOM nodes created:

 componentDidMount() {
    fetch('data.csv')
   .then(res => res.json())
   .then(data => {
       this.allMarkers = data.items.map(v => new LatLng(v.lat, v.lng));
       displayMarkers();
    });
 }

where

displayMarkers() {
   const map = this.mapRef.current.leafletElement;
   const markers = this.allMarkers.filter(m =>
      map.getBounds().contains(m)
   );

   this.setState({
       markers: markers
   });
}

Demo

Another optimization (leaflet specific) would be to set preferCanvas to true to render markers on canvas instead of SVG:

Whether Paths should be rendered on a Canvas renderer. By default, all Paths are rendered in a SVG renderer.

<Map
    preferCanvas={true}
    center={new LatLng(51.505, -0.09)}
    zoom={this.state.zoom}
  >
    ... 
  </Map>

The following demo demonstrates how to render 20k markers via react-leaflet

Cochlea answered 2/1, 2019 at 13:0 Comment(3)
That preferCanvas is a great thing. Thanks for letting us know.Dhaulagiri
Hi, do you think having a custom marker, and update it's size depending on zoom level would bring similar performances? CircleMarker takes a "radius" input, which makes it automatically resized but it's hard to achieve with a custom shape. Basically I wonder if your solution scales if a rerender of the marker is needed at each pan or at least zoom.Lavettelavigne
Unfortunately this solution (including the stackblitz demo) is terribly laggy, especially as you zoom out and pan around. One reason is that every time you fire map moveend, you return a new array of markers through the use of filter and map, and react-leaflet will draw all those markers again. This is the case even with react 18 and react-leaflet 4. Memoizing each rendered marker according to a unique id helps, but even so, the react handlers that wrap the leaflet L.marker seem to slow down the rendering significantly, and I still don't understand whyLambeth
O
1

My problem was slightly different in that the points rendered in an acceptable period of time, but the pan and zoom are deathly slow. My native javascript application works fine. None of the suggestions here helped.

Why is Leaflet so slow at pan and zoom inside React?

I solved the problem by putting my native javascript map application inside an iframe on the react page. It is not an ideal solution and my hope is that a better one presents itself, but my guess is that it will require an update to either leaflet or react.

Officialism answered 5/5, 2020 at 18:1 Comment(0)
R
0

How I solved it was by overriding shouldComponentUpdate lifeCycle method, The issue with a component like maps are they are usually too heavy. So if you have a lot of markers and maps lee[pm rerendering you should override the shouldComponent lifecycle method. This way you ensure that the map is re-rendered(expensive operation) only when the necessary properties are changed. It will help.

shouldComponentUpdate(prevProps) {
 return !areEqual(this.props, prevProps);
}
Ravishment answered 29/4, 2020 at 5:32 Comment(1)
Hi Ishaan, I am specifically using your awesome enhanced markers. The problem is that you may have legitimate prop change, like updating the dimension of the marker depending on the zoom levelLavettelavigne

© 2022 - 2024 — McMap. All rights reserved.