You are recreating the Select
interaction component on each render, this is why you lose the handlers. Moving it out of the component fixes your problem.
I also suggest you take a look at https://github.com/mmomtchev/rlayers (of which I am the author) instead of trying to reinvent the wheel. I already spent many hours fixing such problems.
import React, { useRef, useState, useEffect } from 'react';
import Map from 'ol/Map';
import View from 'ol/View';
import OlLayerTile from 'ol/layer/Tile';
import XYZ from 'ol/source/XYZ';
import Select from 'ol/interaction/Select';
import OlGeoJSON from 'ol/format/GeoJSON';
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import './style.css';
const initLayers = [
new OlLayerTile({
source: new XYZ({
url: 'http://mt0.google.com/vt/lyrs=s&hl=en&x={x}&y={y}&z={z}',
}),
}),
];
const select = new Select({
hitTolerance: 4,
});
export default () => {
const mapElement = useRef<HTMLDivElement | null>(null);
const [map, setMap] = useState<Map | undefined>();
const [mapLayers, setMapLayers] = useState<string[]>([]);
useEffect(() => {
const newMap = initMap(mapElement);
newMap.addInteraction(select);
const geojson = {
type: 'Polygon',
coordinates: [
[
[30.0, 10.0],
[40.0, 40.0],
[20.0, 40.0],
[10.0, 20.0],
[30.0, 10.0],
],
],
};
let format = new OlGeoJSON();
let features = format.readFeatures(geojson);
let source = new VectorSource({
features: features,
});
let layer = new VectorLayer({
source: source,
});
newMap.addLayer(layer);
setMap(newMap);
}, []);
function initMap(
mapElement: React.MutableRefObject<HTMLDivElement | null>
): Map | undefined {
if (!mapElement.current) return;
const view = new View({
projection: 'EPSG:4326',
center: [30.0, 10.0],
zoom: 3,
});
const map = new Map({
layers: initLayers,
view: view,
controls: [],
});
map.setTarget(mapElement.current);
return map;
}
useEffect(() => {
console.log('Map Layer Change:', mapLayers);
select.getFeatures().un(['add'], selectAddCallback);
select.getFeatures().on(['add'], selectAddCallback);
}, [mapLayers]);
const handleClick = () => {
setMapLayers((prevList) => [...prevList, 'New Item']);
};
const selectAddCallback = () => {
console.log('Select Callback');
console.log(mapLayers);
};
return (
<div className="container">
<div id="Map-provider" ref={mapElement} className="map" />
<button className="button" onClick={handleClick}>
Add
</button>
</div>
);
};