4

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!

1
  • I would say, it depends on your use case. E.g. you have 3 markers and every time you update their positions. So you can apply setLatLng() on the specific marker (this is pure leaflet function, you should have similar function in react-leaflet). In other case, you receive new markers, not the 3 old ones. It could be 2, 4, 5. So they have nothing to do with the old one. In this case, you should remove all markers and add new ones like your approach. And of course it will be not performant but it's the only way to go.
    – tomen
    Commented Jan 21, 2020 at 22:01

2 Answers 2

5

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.

0

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.

Not the answer you're looking for? Browse other questions tagged or ask your own question.