Version: "ol": "^6.14.1",
I am trying to add a marker to an OpenLayer map in React, but whenever I click on the map I get the error: Uncaught AssertionError: Assertion failed. See https://openlayers.org/en/v6.14.1/doc/errors/#58 for details. which comes from the code map.addLayer(newMarkersLayer);.
The error states that: Duplicate item added to a unique collection. For example, it may be that you tried to add the same layer to a map twice. Check for calls to map.addLayer() or other places where the map's layer collection is modified.
This duplication happens because React.StricMode is wrapped around my app. If I disable strict mode, clicking on the map adds a marker.
I don't want to disable strict mode, because I still think that the error comes from my way of implementing the map in React using a badly written useEffect().
How should I add a marker on an OpenLayers map in React so that StrictMode doesn't adds duplicates ?
Map component:
import { useState, useEffect, useRef } from 'react';
// import ModalUI from '../UI/ModalUI';
import classes from './MapUI.module.css';
import { drawerActions } from '../Rooms/Drawers/drawerSlice';
import 'ol/ol.css';
import { Map, View, Overlay, Feature } from 'ol';
import Point from 'ol/geom/Point';
import { Vector as VectorLayer } from 'ol/layer';
import VectorSource from 'ol/source/Vector';
import { fromLonLat, toLonLat } from 'ol/proj';
import { toStringHDMS } from 'ol/coordinate';
import TileLayer from 'ol/layer/Tile';
import OSM from 'ol/source/OSM';
import PopUp from './PopUp';
import { useDispatch } from 'react-redux';
export default function MapUI() {
const dispatch = useDispatch();
const mapRef = useRef();
const popup = useRef();
const [coordinates, setCoordinates] = useState('');
const [newMarker, setNewMarker] = useState(
new Feature({
geometry: new Point([[]]),
name: '',
})
);
const [newMarkersLayer] = useState(
new VectorLayer({
properties: { name: 'newMarkers' },
source: new VectorSource({
features: [newMarker],
}),
})
);
const closePopup = () => {
map.getOverlayById('map-popup').setPosition(undefined);
map.removeLayer(newMarkersLayer);
};
const [map] = useState(
new Map({
target: '',
layers: [
new TileLayer({
source: new OSM(),
}),
new VectorLayer({
properties: { name: 'existingMarkers' },
source: new VectorSource({
// features: [marker],
}),
}),
],
view: new View({
center: fromLonLat([26.08, 44.46]),
zoom: 15,
minZoom: 10,
maxZoom: 20,
}),
})
);
useEffect(() => {
const overlay = new Overlay({
element: popup.current,
id: 'map-popup',
autoPan: {
animation: {
duration: 250,
},
},
});
// console.log('useEffect in MapUI.jsx');
map.addOverlay(overlay);
map.setTarget(mapRef.current);
map.on('singleclick', function (evt) {
map.addLayer(newMarkersLayer);
dispatch(drawerActions.closeDrawer());
newMarker.getGeometry().setCoordinates(evt.coordinate);
// console.log(typeof evt.coordinate);
setCoordinates(toStringHDMS(toLonLat(evt.coordinate)));
overlay.setPosition(evt.coordinate);
});
}, [newMarkersLayer, map, newMarker, dispatch]);
return (
<>
<div
style={{ height: '100%', width: '100%' }}
ref={mapRef}
className='map-container'
/>
<div id='map-popup' className={classes['ol-popup']} ref={popup}>
<PopUp coordinates={coordinates} closePopup={closePopup} />
</div>
</>
);
}