Pedro Brost
Pedro Brost

Reputation: 1452

React-leaflet: how to update marker positions?

I can't understand how to update my markers correctly using react-leaflet. Using this example:

import React from 'react';
import { render } from 'react-dom';
import { connect } from 'react-redux';
import { Map, Marker, TileLayer } from 'react-leaflet';

const map = props => (
  <Map center={[51.505, -0.09]} zoom={13}>
    <TileLayer
      url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
      attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
    />
    {props.markers.map(m => (
      <Marker position={m.position} />
    ))}
  </Map>
);

const mapStateToProps = state => ({
  markers: state.markers // array of objects with positions. this can change
});

render(connect(mapStateToProps)(map), document.getElementById('map-container'));

This works, but I don't know if it's the correct way of doing it. Because in that case when the markers update their position (or there are more markers), Leaflet will remove the markers and put new ones, instead of updating the position of the original markers.

So my question is that. Am I doing it right or this is not the most performant way?

Thanks!

Upvotes: 4

Views: 7821

Answers (2)

user10609288
user10609288

Reputation:

You can use react hooks if you are using react 16+.

function Map() {
  let [map, setMap] = useState(null);

  const [, updateState] = React.useState();
  const forceUpdate = React.useCallback(() => updateState({}), []);
  console.log('defaultPosition', defaultPosition);
  return (
    <div>
      <div className="map__container" onMouseUp={forceUpdate}>
        <MapContainer center={defaultPosition} zoom={13} whenCreated={setMap}>
          <TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
          <Marker position={defaultPosition}></Marker>;
        </MapContainer>
      </div>
      {map ? <DisplayPosition map={map} /> : null}
    </div>
  );
}

3 and 4 line call hook update state, and updated defaultPosition come in Marker component and rerender it.

Code before:

import {
  MapContainer,
  TileLayer,
  Marker,
  MapConsumer,
  useMapEvent,
} from 'react-leaflet';

let defaultPosition = [48.864719, 2.349]; // Paris position
let [position, setPosition] = ['', ''];

function DisplayPosition({ map }) {
  [position, setPosition] = useState(map.getCenter());
  map
    ? (defaultPosition = [
        Number(map.getCenter().lat.toFixed(4)),
        Number(map.getCenter().lng.toFixed(4)),
      ])
    : [48.864719, 2.349];
  const onClick = useCallback(() => {
    map.setView([48.864716, 2.349], 13);
  }, [map]);
  // console.log('markerPos', markerPos);
  const onMove = useCallback(() => {
    setPosition(map.getCenter());
  }, [map]);

  useEffect(() => {
    map.on('move', onMove);
    return () => {
      map.off('move', onMove);
    };
  }, [map, onMove]);

  return (
    <p>
      latitude: {position.lat.toFixed(4)}, longitude: {position.lng.toFixed(4)}{' '}
      <button onClick={onClick}>reset</button>
    </p>
  );
}

Without hooks it's impossible.

Upvotes: -1

Prasanna
Prasanna

Reputation: 4656

Am I doing it right?

Yes, you are. You are supposed to use it this way.

this is not the most performant way

You are right there as well. This is not the most performant way. It is because once the props change, it will force a re-render of the react component. That will include the removal of the markers that were present and the addition of all the new markers. This is because React will have no idea on what is from the previous props and what are the new things.

You can fix that by using keys while mapping your data. Read more about it here

Another way would be to calculate your addedMarkers, removedMarkers and updatedMarkers separately and use them to re-render your view. That, however, can turn out to be a big hassle if there are a fairly lesser number of markers your app is going to use.

Upvotes: 5

Related Questions