Reputation: 13542
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%' }} />
);
}
}
Upvotes: 17
Views: 11541
Reputation: 1103
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.
Upvotes: 1
Reputation: 26
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);
}
Upvotes: 0
Reputation: 59348
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
});
}
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
Upvotes: 11